diff options
11 files changed, 377 insertions, 178 deletions
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 426a1a4fc4..6bdc8cf911 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 @@ -42,9 +42,4 @@ public interface XDDFCategoryDataSource extends XDDFDataSource<String> { default boolean isReference() { return true; } - - @Override - default String getDataRangeReference() { - return getFormula(); - } } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java index 88592c0843..7b29b33a28 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -124,10 +124,6 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ protected final CTChartSpace chartSpace; - /** - * Chart element in the chart space - */ - protected final CTChart chart; /** * Construct a chart. @@ -136,8 +132,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai super(); chartSpace = CTChartSpace.Factory.newInstance(); - chart = chartSpace.addNewChart(); - chart.addNewPlotArea(); + chartSpace.addNewChart().addNewPlotArea(); } /** @@ -153,7 +148,6 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai super(part); chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace(); - chart = chartSpace.getChart(); } /** @@ -174,8 +168,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ @Internal public CTChart getCTChart() { - return chart; - + return chartSpace.getChart(); } /** @@ -185,7 +178,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ @Internal protected CTPlotArea getCTPlotArea() { - return chart.getPlotArea(); + return getCTChart().getPlotArea(); } /** @@ -199,8 +192,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai workbook.removeSheetAt(0); workbook.createSheet(); } - chart.set(CTChart.Factory.newInstance()); - chart.addNewPlotArea(); + getCTChart().set(CTChart.Factory.newInstance()); + getCTChart().addNewPlotArea(); } /** @@ -208,8 +201,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * otherwise */ public boolean isPlotOnlyVisibleCells() { - if (chart.isSetPlotVisOnly()) { - return chart.getPlotVisOnly().getVal(); + if (getCTChart().isSetPlotVisOnly()) { + return getCTChart().getPlotVisOnly().getVal(); } else { return false; } @@ -221,40 +214,40 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * the chart */ public void setPlotOnlyVisibleCells(boolean only) { - if (!chart.isSetPlotVisOnly()) { - chart.setPlotVisOnly(CTBoolean.Factory.newInstance()); + if (!getCTChart().isSetPlotVisOnly()) { + getCTChart().setPlotVisOnly(CTBoolean.Factory.newInstance()); } - chart.getPlotVisOnly().setVal(only); + getCTChart().getPlotVisOnly().setVal(only); } public void setFloor(int thickness) { - if (!chart.isSetFloor()) { - chart.setFloor(CTSurface.Factory.newInstance()); + if (!getCTChart().isSetFloor()) { + getCTChart().setFloor(CTSurface.Factory.newInstance()); } - chart.getFloor().getThickness().setVal(thickness); + getCTChart().getFloor().getThickness().setVal(thickness); } public void setBackWall(int thickness) { - if (!chart.isSetBackWall()) { - chart.setBackWall(CTSurface.Factory.newInstance()); + if (!getCTChart().isSetBackWall()) { + getCTChart().setBackWall(CTSurface.Factory.newInstance()); } - chart.getBackWall().getThickness().setVal(thickness); + getCTChart().getBackWall().getThickness().setVal(thickness); } public void setSideWall(int thickness) { - if (!chart.isSetSideWall()) { - chart.setSideWall(CTSurface.Factory.newInstance()); + if (!getCTChart().isSetSideWall()) { + getCTChart().setSideWall(CTSurface.Factory.newInstance()); } - chart.getSideWall().getThickness().setVal(thickness); + getCTChart().getSideWall().getThickness().setVal(thickness); } public void setAutoTitleDeleted(boolean deleted) { - if (!chart.isSetAutoTitleDeleted()) { - chart.setAutoTitleDeleted(CTBoolean.Factory.newInstance()); + if (!getCTChart().isSetAutoTitleDeleted()) { + getCTChart().setAutoTitleDeleted(CTBoolean.Factory.newInstance()); } - chart.getAutoTitleDeleted().setVal(deleted); - if (deleted && chart.isSetTitle()) { - chart.unsetTitle(); + getCTChart().getAutoTitleDeleted().setVal(deleted); + if (deleted && getCTChart().isSetTitle()) { + getCTChart().unsetTitle(); } } @@ -264,14 +257,14 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ public void displayBlanksAs(DisplayBlanks as) { if (as == null){ - if (chart.isSetDispBlanksAs()) { - chart.unsetDispBlanksAs(); + if (getCTChart().isSetDispBlanksAs()) { + getCTChart().unsetDispBlanksAs(); } } else { - if (chart.isSetDispBlanksAs()) { - chart.getDispBlanksAs().setVal(as.underlying); + if (getCTChart().isSetDispBlanksAs()) { + getCTChart().getDispBlanksAs().setVal(as.underlying); } else { - chart.addNewDispBlanksAs().setVal(as.underlying); + getCTChart().addNewDispBlanksAs().setVal(as.underlying); } } } @@ -280,8 +273,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * @since 4.0.1 */ public Boolean getTitleOverlay() { - if (chart.isSetTitle()) { - CTTitle title = chart.getTitle(); + if (getCTChart().isSetTitle()) { + CTTitle title = getCTChart().getTitle(); if (title.isSetOverlay()) { return title.getOverlay().getVal(); } @@ -293,10 +286,10 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * @since 4.0.1 */ public void setTitleOverlay(boolean overlay) { - if (!chart.isSetTitle()) { - chart.addNewTitle(); + if (!getCTChart().isSetTitle()) { + getCTChart().addNewTitle(); } - new XDDFTitle(this, chart.getTitle()).setOverlay(overlay); + new XDDFTitle(this, getCTChart().getTitle()).setOverlay(overlay); } /** @@ -307,18 +300,18 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * @since 4.0.1 */ public void setTitleText(String text) { - if (!chart.isSetTitle()) { - chart.addNewTitle(); + if (!getCTChart().isSetTitle()) { + getCTChart().addNewTitle(); } - new XDDFTitle(this, chart.getTitle()).setText(text); + new XDDFTitle(this, getCTChart().getTitle()).setText(text); } /** * @since 4.0.1 */ public XDDFTitle getTitle() { - if (chart.isSetTitle()) { - return new XDDFTitle(this, chart.getTitle()); + if (getCTChart().isSetTitle()) { + return new XDDFTitle(this, getCTChart().getTitle()); } else { return null; } @@ -339,10 +332,10 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ public XDDFView3D getOrAddView3D() { CTView3D view3D; - if (chart.isSetView3D()) { - view3D = chart.getView3D(); + if (getCTChart().isSetView3D()) { + view3D = getCTChart().getView3D(); } else { - view3D = chart.addNewView3D(); + view3D = getCTChart().addNewView3D(); } return new XDDFView3D(view3D); } @@ -355,10 +348,10 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai */ @Beta public XDDFTextBody getFormattedTitle() { - if (!chart.isSetTitle()) { + if (!getCTChart().isSetTitle()) { return null; } - return new XDDFTitle(this, chart.getTitle()).getBody(); + return new XDDFTitle(this, getCTChart().getTitle()).getBody(); } @Override @@ -393,12 +386,12 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai } public XDDFChartLegend getOrAddLegend() { - return new XDDFChartLegend(chart); + return new XDDFChartLegend(getCTChart()); } public void deleteLegend() { - if (chart.isSetLegend()) { - chart.unsetLegend(); + if (getCTChart().isSetLegend()) { + getCTChart().unsetLegend(); } } @@ -418,7 +411,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai series.plot(); XDDFDataSource<?> categoryDS = series.getCategoryData(); XDDFNumericalDataSource<? extends Number> valuesDS = series.getValuesData(); - if (categoryDS.isCellRange() || valuesDS.isCellRange() + if (categoryDS == null || valuesDS == null + || categoryDS.isCellRange() || valuesDS.isCellRange() || categoryDS.isLiteral() || valuesDS.isLiteral()) { // let's assume the data is already in the sheet } else { @@ -910,7 +904,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai * @since POI 4.0.0 */ public void importContent(XDDFChart other) { - this.chart.set(other.chart); + getCTChartSpace().set(other.getCTChartSpace()); } /** @@ -1074,12 +1068,51 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai } /** - * set chart index which can be use for relation part + * Set chart index which can be used for relation part. * * @param chartIndex - * chart index which can be use for relation part + * chart index which can be used for relation part. */ public void setChartIndex(int chartIndex) { this.chartIndex = chartIndex; } + + /** + * Replace references to the sheet name in the data supporting the chart. + * + * @param newSheet + * sheet to be used in the data references. + * + * @since POI 5.0.1 + */ + public void replaceReferences(XSSFSheet newSheet) { + for (XDDFChartData data : getChartSeries()) { + for (XDDFChartData.Series series : data.series) { + XDDFDataSource newCategory = series.categoryData; + XDDFNumericalDataSource newValues = series.valuesData; + try { + if (series.categoryData != null && series.categoryData.isReference()) { + String ref = series.categoryData.getDataRangeReference(); + CellRangeAddress rangeAddress = CellRangeAddress.valueOf(ref.substring(ref.indexOf('!') + 1)); + newCategory = series.categoryData.isNumeric() + ? XDDFDataSourcesFactory.fromNumericCellRange(newSheet, rangeAddress) + : XDDFDataSourcesFactory.fromStringCellRange(newSheet, rangeAddress); + if (newCategory.isNumeric()) { + ((XDDFNumericalDataSource) newCategory).setFormatCode(series.categoryData.getFormatCode()); + } + } + if (series.valuesData!= null && series.valuesData.isReference()) { + String ref = series.valuesData.getDataRangeReference(); + CellRangeAddress rangeAddress = CellRangeAddress.valueOf(ref.substring(ref.indexOf('!') + 1)); + newValues = XDDFDataSourcesFactory.fromNumericCellRange(newSheet, rangeAddress); + newValues.setFormatCode(series.valuesData.getFormatCode()); + } + } catch (IllegalArgumentException iae) { + // keep old values when cell range cannot be parsed + } + series.replaceData(newCategory, newValues); + series.plot(); + } + } + } } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java index 937ed1bbb6..225749f508 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java @@ -162,12 +162,11 @@ public abstract class XDDFChartData { } public void replaceData(XDDFDataSource<?> category, XDDFNumericalDataSource<? extends Number> values) { - if (category == null || values == null) { - throw new IllegalStateException("Category and values must be defined before filling chart data."); - } - int numOfPoints = category.getPointCount(); - if (numOfPoints != values.getPointCount()) { - throw new IllegalStateException("Category and values must have the same point count."); + if (categoryData != null && values != null) { + int numOfPoints = category.getPointCount(); + if (numOfPoints != values.getPointCount()) { + throw new IllegalStateException("Category and values must have the same point count."); + } } this.categoryData = category; this.valuesData = values; @@ -209,15 +208,19 @@ public abstract class XDDFChartData { } public void plot() { - if (categoryData.isNumeric()) { - CTNumData cache = retrieveNumCache(getAxDS(), categoryData); - categoryData.fillNumericalCache(cache); - } else { - CTStrData cache = retrieveStrCache(getAxDS(), categoryData); - categoryData.fillStringCache(cache); + if (categoryData != null) { + if (categoryData.isNumeric()) { + CTNumData cache = retrieveNumCache(getAxDS(), categoryData); + categoryData.fillNumericalCache(cache); + } else { + CTStrData cache = retrieveStrCache(getAxDS(), categoryData); + categoryData.fillStringCache(cache); + } + } + if (valuesData != null) { + CTNumData cache = retrieveNumCache(getNumDS(), valuesData); + valuesData.fillNumericalCache(cache); } - CTNumData cache = retrieveNumCache(getNumDS(), valuesData); - valuesData.fillNumericalCache(cache); } /** 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 439d0a325c..ea99a34b12 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 @@ -50,7 +50,9 @@ public interface XDDFDataSource<T> { String getDataRangeReference(); - String getFormula(); + default String getFormula() { + return getDataRangeReference(); + } String getFormatCode(); 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 c45fbaa6a7..4cd3bc876f 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 @@ -42,7 +42,10 @@ public class XDDFDataSourcesFactory { } public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { - if (categoryDS.getStrRef() == null) { + if (categoryDS == null) { + return null; + } + if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { return new XDDFCategoryDataSource() { private CTNumData category = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); private String formatCode = category.isSetFormatCode() ? category.getFormatCode() : null; @@ -58,7 +61,7 @@ public class XDDFDataSourcesFactory { } @Override - public String getFormula() { + public String getDataRangeReference() { return categoryDS.getNumRef().getF(); } @@ -75,7 +78,7 @@ public class XDDFDataSourcesFactory { @Override public String getFormatCode() { return formatCode; } }; - } else { + } else if (categoryDS.getStrRef() != null && categoryDS.getStrRef().getStrCache() != null) { return new XDDFCategoryDataSource() { private CTStrData category = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); @@ -85,7 +88,7 @@ public class XDDFDataSourcesFactory { } @Override - public String getFormula() { + public String getDataRangeReference() { return categoryDS.getStrRef().getF(); } @@ -102,64 +105,203 @@ public class XDDFDataSourcesFactory { @Override public String getFormatCode() { return null; } }; + } else if (categoryDS.getNumLit() != null) { + return new XDDFCategoryDataSource() { + private CTNumData category = (CTNumData) categoryDS.getNumLit().copy(); + private String formatCode = category.isSetFormatCode() ? category.getFormatCode() : null; + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return false; + } + + @Override + public String getDataRangeReference() { + return null; + } + + @Override + public int getPointCount() { + return (int) category.getPtCount().getVal(); + } + + @Override + public String getPointAt(int index) { + return category.getPtArray(index).getV(); + } + + @Override + public String getFormatCode() { return formatCode; } + }; + } else if (categoryDS.getStrLit() != null) { + return new XDDFCategoryDataSource() { + private CTStrData category = (CTStrData) categoryDS.getStrLit().copy(); + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public boolean isReference() { + return false; + } + + @Override + public String getDataRangeReference() { + return null; + } + + @Override + public int getPointCount() { + return (int) category.getPtCount().getVal(); + } + + @Override + public String getPointAt(int index) { + return category.getPtArray(index).getV(); + } + + @Override + public String getFormatCode() { return null; } + }; + } else { + return null; // in some weird cases the element is empty } } public static XDDFNumericalDataSource<Double> fromDataSource(final CTNumDataSource valuesDS) { - return new XDDFNumericalDataSource<Double>() { - private CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); - private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + if (valuesDS == null) { + return null; + } + if (valuesDS.getNumRef() != null && valuesDS.getNumRef().getNumCache() != null) { + return new XDDFNumericalDataSource<Double>() { + 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; + } - @Override - public String getFormatCode() { - return formatCode; - } + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } + @Override + public boolean isCellRange() { + return true; + } - @Override - public boolean isCellRange() { - return true; - } + @Override + public boolean isNumeric() { + return true; + } - @Override - public boolean isNumeric() { - return true; - } + @Override + public boolean isReference() { + return true; + } - @Override - public boolean isReference() { - return true; - } + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } - @Override - public int getPointCount() { - return (int) values.getPtCount().getVal(); - } + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } - @Override - public Double getPointAt(int index) { - return Double.valueOf(values.getPtArray(index).getV()); - } + @Override + public String getDataRangeReference() { + return valuesDS.getNumRef().getF(); + } - @Override - public String getDataRangeReference() { - return valuesDS.getNumRef().getF(); - } + @Override + public int getColIndex() { + return 0; + } + }; + } else if (valuesDS.getNumLit() != null) { + return new XDDFNumericalDataSource<Double>() { + private CTNumData values = (CTNumData) valuesDS.getNumLit().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; - @Override - public int getColIndex() { - return 0; - } - }; + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return false; + } + + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } + + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } + + @Override + public String getDataRangeReference() { + return null; + } + + @Override + public int getColIndex() { + return 0; + } + }; + } else { + return null; // in some weird cases the element is empty + } } public static <T extends Number> XDDFNumericalDataSource<T> fromArray(T[] elements) { @@ -265,11 +407,6 @@ public class XDDFDataSourcesFactory { } @Override - public String getFormula() { - return getDataRangeReference(); - } - - @Override public String getFormatCode() { return formatCode; } @@ -291,11 +428,6 @@ public class XDDFDataSourcesFactory { } @Override - public String getFormula() { - return getDataRangeReference(); - } - - @Override public String getFormatCode() { return null; } } @@ -382,11 +514,6 @@ public class XDDFDataSourcesFactory { super(sheet, cellRangeAddress); } - @Override - public String getFormula() { - return getDataRangeReference(); - } - private String formatCode; @Override @@ -422,11 +549,6 @@ public class XDDFDataSourcesFactory { } @Override - public String getFormula() { - return getDataRangeReference(); - } - - @Override public String getPointAt(int index) { CellValue cellValue = getCellValueAt(index); if (cellValue != null && cellValue.getCellType() == CellType.STRING) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java index 97f3ec23ee..c54cba50c8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java @@ -84,10 +84,10 @@ public final class XSLFChart extends XDDFChart { } public XSLFTextShape getTitleShape() { - if (!chart.isSetTitle()) { - chart.addNewTitle(); + if (!getCTChart().isSetTitle()) { + getCTChart().addNewTitle(); } - final CTTitle title = chart.getTitle(); + final CTTitle title = getCTChart().getTitle(); if (title.getTx() != null && title.getTx().isSetRich()) { return new XSLFTextShape(title, null) { @Override diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java index 428d74e96a..e5646a41d6 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java @@ -91,7 +91,7 @@ public final class XSSFChart extends XDDFChart { CTPlotArea plotArea = getCTPlotArea(); plotArea.addNewLayout(); - chart.addNewPlotVisOnly().setVal(true); + getCTChart().addNewPlotVisOnly().setVal(true); CTPrintSettings printSettings = chartSpace.addNewPrintSettings(); printSettings.addNewHeaderFooter(); @@ -154,12 +154,12 @@ public final class XSSFChart extends XDDFChart { * if the title text is empty or the title uses a formula instead */ public XSSFRichTextString getTitleText() { - if (!chart.isSetTitle()) { + if (!getCTChart().isSetTitle()) { return null; } // TODO Do properly - CTTitle title = chart.getTitle(); + CTTitle title = getCTChart().getTitle(); StringBuilder text = new StringBuilder(64); XmlObject[] t = title.selectPath("declare namespace a='" + XSSFDrawing.NAMESPACE_A + "' .//a:t"); @@ -183,11 +183,11 @@ public final class XSSFChart extends XDDFChart { * @return formula expression or null */ public String getTitleFormula() { - if (!chart.isSetTitle()) { + if (!getCTChart().isSetTitle()) { return null; } - CTTitle title = chart.getTitle(); + CTTitle title = getCTChart().getTitle(); if (!title.isSetTx()) { return null; @@ -209,10 +209,10 @@ public final class XSSFChart extends XDDFChart { */ public void setTitleFormula(String formula) { CTTitle ctTitle; - if (chart.isSetTitle()) { - ctTitle = chart.getTitle(); + if (getCTChart().isSetTitle()) { + ctTitle = getCTChart().getTitle(); } else { - ctTitle = chart.addNewTitle(); + ctTitle = getCTChart().addNewTitle(); } CTTx tx; diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index fadb24dae8..8b437cb222 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -229,10 +229,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS * @see org.apache.poi.xssf.usermodel.XSSFDrawing#createChart(ClientAnchor) */ public XSSFChart createChart(XSSFClientAnchor anchor) { - int chartNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.CHART.getContentType()) - .size() + 1; - - RelationPart rp = createRelationship(XSSFRelation.CHART, XSSFFactory.getInstance(), chartNumber, false); + RelationPart rp = createChartRelationPart(); XSSFChart chart = rp.getDocumentPart(); String chartRelId = rp.getRelationship().getId(); @@ -243,6 +240,13 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS return chart; } + protected RelationPart createChartRelationPart() { + int chartNumber = getPackagePart().getPackage().getPartsByContentType(XSSFRelation.CHART.getContentType()) + .size() + 1; + + return createRelationship(XSSFRelation.CHART, XSSFFactory.getInstance(), chartNumber, false); + } + /** * Creates a chart. * @@ -271,7 +275,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS XSSFClientAnchor destAnchor = new XSSFClientAnchor(from, to); destAnchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); XSSFChart destChart = createChart(destAnchor); - destChart.getCTChart().set(srcChart.getCTChartSpace().getChart().copy()); + destChart.getCTChartSpace().set(srcChart.getCTChartSpace().copy()); return destChart; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index e390bca2ca..fc019a8f30 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -671,9 +671,18 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Date1904Su clonedDg.getCTDrawing().set(dg.getCTDrawing().copy()); // Clone drawing relations - List<RelationPart> srcRels = srcSheet.createDrawingPatriarch().getRelationParts(); + List<RelationPart> srcRels = srcSheet.getDrawingPatriarch().getRelationParts(); for (RelationPart rp : srcRels) { - addRelation(rp, clonedDg); + POIXMLDocumentPart r = rp.getDocumentPart(); + if (r instanceof XSSFChart) { + // Replace chart relation part with new relationship, cloning the chart's content + RelationPart chartPart = clonedDg.createChartRelationPart(); + XSSFChart chart = chartPart.getDocumentPart(); + chart.importContent((XSSFChart)r); + chart.replaceReferences(clonedSheet); + } else { + addRelation(rp, clonedDg); + } } } return clonedSheet; diff --git a/src/ooxml/testcases/org/apache/poi/xssf/TestXSSFCloneSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/TestXSSFCloneSheet.java index 2858429389..b3c2896fd8 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/TestXSSFCloneSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/TestXSSFCloneSheet.java @@ -18,14 +18,16 @@ package org.apache.poi.xssf; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.fail; import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.ss.usermodel.BaseTestCloneSheet; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.jupiter.api.BeforeEach; @@ -87,4 +89,33 @@ class TestXSSFCloneSheet extends BaseTestCloneSheet { assertNotNull(source_sh); } } + + @Test + void testBug63902() throws IOException { + try (XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx")) { + XSSFSheet sheet = workbook.getSheetAt(0); + XSSFDrawing drawing = sheet.getDrawingPatriarch(); + assertEquals(1, drawing.getCharts().size()); + + XSSFSheet sheet2 = workbook.cloneSheet(0, "Sheet 2"); + XSSFDrawing drawing2 = sheet2.getDrawingPatriarch(); + assertEquals(1, drawing2.getCharts().size()); + assertEquals("Sheet 2", sheet2.getSheetName()); + + XDDFDataSource<?> data = drawing.getCharts().get(0).getChartSeries().get(0).getSeries(0).getCategoryData(); + XDDFDataSource<?> data2 = drawing2.getCharts().get(0).getChartSeries().get(0).getSeries(0).getCategoryData(); + assertNotEquals(data.getFormula(), data2.getFormula()); + assertEquals(sheet.getSheetName(), data.getFormula().substring(0, data.getFormula().indexOf('!'))); + assertEquals("'Sheet 2'", data2.getFormula().substring(0, data2.getFormula().indexOf('!'))); + assertEquals( + data.getFormula().substring(data.getFormula().indexOf('!')), + data2.getFormula().substring(data2.getFormula().indexOf('!')) + ); + + Workbook wbBack = XSSFTestDataSamples.writeOutAndReadBack(workbook, "poi_cloned_sheet_with_chart"); + assertNotNull(wbBack); + wbBack.close(); + } + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java index e000ed3dc9..c072013404 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java @@ -914,19 +914,19 @@ class TestXSSFDrawing { } @Test - void testBug63901() { - XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx"); - XSSFSheet sheet = workbook.getSheet("Sheet1"); - XSSFDrawing drawing = sheet.createDrawingPatriarch(); - assert(drawing.getCharts().size() > 0); - - XSSFWorkbook workbook2 = new XSSFWorkbook(); - XSSFSheet sheet2 = workbook2.createSheet(); - XSSFDrawing drawing2 = sheet2.createDrawingPatriarch(); - - drawing.getCharts().forEach(drawing2::importChart); - - assertEquals(drawing.getCharts().size(), drawing2.getCharts().size()); + void testBug63901() throws IOException { + try (XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx")) { + XSSFSheet sheet = workbook.getSheetAt(0); + XSSFDrawing drawing = sheet.getDrawingPatriarch(); + assertEquals(1, drawing.getCharts().size()); + + XSSFWorkbook workbook2 = new XSSFWorkbook(); + XSSFSheet sheet2 = workbook2.createSheet(); + XSSFDrawing drawing2 = sheet2.createDrawingPatriarch(); + + drawing.getCharts().forEach(drawing2::importChart); + assertEquals(1, drawing2.getCharts().size()); + } } private static void checkRewrite(XSSFWorkbook wb) throws IOException { |