/* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== */ package org.apache.poi.xddf.usermodel.chart; import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import javax.xml.namespace.QName; import org.apache.poi.ooxml.POIXMLDocument; import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.ooxml.POIXMLFactory; import org.apache.poi.ooxml.POIXMLRelation; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.poi.xddf.usermodel.XDDFShapeProperties; import org.apache.poi.xddf.usermodel.text.TextContainer; import org.apache.poi.xddf.usermodel.text.XDDFTextBody; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.chart.CTArea3DChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTAreaChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBar3DChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace; import org.openxmlformats.schemas.drawingml.x2006.chart.CTDateAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTDoughnutChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTExternalData; import org.openxmlformats.schemas.drawingml.x2006.chart.CTLine3DChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPie3DChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; import org.openxmlformats.schemas.drawingml.x2006.chart.CTRadarChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurface; import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurface3DChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurfaceChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTView3D; import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; @Beta public abstract class XDDFChart extends POIXMLDocumentPart implements TextContainer { /** * default width of chart in emu */ public static final int DEFAULT_WIDTH = 500000; /** * default height of chart in emu */ public static final int DEFAULT_HEIGHT = 500000; /** * default x-coordinate of chart in emu */ public static final int DEFAULT_X = 10; /** * default y-coordinate value of chart in emu */ public static final int DEFAULT_Y = 10; /** * Underlying workbook */ private XSSFWorkbook workbook; private int chartIndex = 0; protected List axes = new ArrayList<>(); /** * Root element of the Chart part */ protected final CTChartSpace chartSpace; /** * Construct a chart. */ protected XDDFChart() { super(); chartSpace = CTChartSpace.Factory.newInstance(); chartSpace.addNewChart().addNewPlotArea(); } /** * Construct a DrawingML chart from a package part. * * @param part * the package part holding the chart data, the content type must * be * {@code application/vnd.openxmlformats-officedocument.drawingml.chart+xml} * @since POI 3.14-Beta1 */ protected XDDFChart(PackagePart part) throws IOException, XmlException { super(part); chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace(); } /** * Return the underlying CTChartSpace bean, the root element of the Chart * part. * * @return the underlying CTChartSpace bean */ @Internal public CTChartSpace getCTChartSpace() { return chartSpace; } /** * Return the underlying CTChart bean, within the Chart Space * * @return the underlying CTChart bean */ @Internal public CTChart getCTChart() { return chartSpace.getChart(); } /** * Return the underlying CTPlotArea bean, within the Chart * * @return the underlying CTPlotArea bean */ @Internal protected CTPlotArea getCTPlotArea() { return getCTChart().getPlotArea(); } /** * Clear all properties, as if a new instance had just been created. * @since POI 4.1.2 */ public void clear() { axes.clear(); seriesCount = 0; if (workbook != null) { workbook.removeSheetAt(0); workbook.createSheet(); } getCTChart().set(CTChart.Factory.newInstance()); getCTChart().addNewPlotArea(); } /** * @return true if only visible cells will be present on the chart, false * otherwise */ public boolean isPlotOnlyVisibleCells() { if (getCTChart().isSetPlotVisOnly()) { return getCTChart().getPlotVisOnly().getVal(); } else { return false; } } /** * @param only * a flag specifying if only visible cells should be present on * the chart */ public void setPlotOnlyVisibleCells(boolean only) { if (!getCTChart().isSetPlotVisOnly()) { getCTChart().setPlotVisOnly(CTBoolean.Factory.newInstance()); } getCTChart().getPlotVisOnly().setVal(only); } public void setFloor(int thickness) { if (!getCTChart().isSetFloor()) { getCTChart().setFloor(CTSurface.Factory.newInstance()); } getCTChart().getFloor().getThickness().setVal(thickness); } public void setBackWall(int thickness) { if (!getCTChart().isSetBackWall()) { getCTChart().setBackWall(CTSurface.Factory.newInstance()); } getCTChart().getBackWall().getThickness().setVal(thickness); } public void setSideWall(int thickness) { if (!getCTChart().isSetSideWall()) { getCTChart().setSideWall(CTSurface.Factory.newInstance()); } getCTChart().getSideWall().getThickness().setVal(thickness); } public void setAutoTitleDeleted(boolean deleted) { if (!getCTChart().isSetAutoTitleDeleted()) { getCTChart().setAutoTitleDeleted(CTBoolean.Factory.newInstance()); } getCTChart().getAutoTitleDeleted().setVal(deleted); if (deleted && getCTChart().isSetTitle()) { getCTChart().unsetTitle(); } } /** * @since 4.0.1 * */ public void displayBlanksAs(DisplayBlanks as) { if (as == null){ if (getCTChart().isSetDispBlanksAs()) { getCTChart().unsetDispBlanksAs(); } } else { if (getCTChart().isSetDispBlanksAs()) { getCTChart().getDispBlanksAs().setVal(as.underlying); } else { getCTChart().addNewDispBlanksAs().setVal(as.underlying); } } } /** * @since 4.0.1 */ public Boolean getTitleOverlay() { if (getCTChart().isSetTitle()) { CTTitle title = getCTChart().getTitle(); if (title.isSetOverlay()) { return title.getOverlay().getVal(); } } return null; } /** * @since 4.0.1 */ public void setTitleOverlay(boolean overlay) { if (!getCTChart().isSetTitle()) { getCTChart().addNewTitle(); } new XDDFTitle(this, getCTChart().getTitle()).setOverlay(overlay); } /** * Sets the title text as a static string. * * @param text * to use as new title * @since 4.0.1 */ public void setTitleText(String text) { if (!getCTChart().isSetTitle()) { getCTChart().addNewTitle(); } new XDDFTitle(this, getCTChart().getTitle()).setText(text); } /** * @since 4.0.1 */ public XDDFTitle getTitle() { if (getCTChart().isSetTitle()) { return new XDDFTitle(this, getCTChart().getTitle()); } else { return null; } } /** * Remove the chart title. * @since POI 5.0.0 */ public void removeTitle() { setAutoTitleDeleted(true); } /** * Get or Add chart 3D view into chart * * @return this method will add 3D view */ public XDDFView3D getOrAddView3D() { CTView3D view3D; if (getCTChart().isSetView3D()) { view3D = getCTChart().getView3D(); } else { view3D = getCTChart().addNewView3D(); } return new XDDFView3D(view3D); } /** * Get the chart title body if there is one, i.e. title is set and is not a * formula. * * @return text body or null, if title is a formula or no title is set. */ @Beta public XDDFTextBody getFormattedTitle() { if (!getCTChart().isSetTitle()) { return null; } return new XDDFTitle(this, getCTChart().getTitle()).getBody(); } @Override public Optional findDefinedParagraphProperty(Predicate isSet, Function getter) { // TODO Auto-generated method stub return Optional.empty(); } @Override public Optional findDefinedRunProperty(Predicate isSet, Function getter) { // TODO Auto-generated method stub return Optional.empty(); } public XDDFShapeProperties getOrAddShapeProperties() { CTPlotArea plotArea = getCTPlotArea(); CTShapeProperties properties; if (plotArea.isSetSpPr()) { properties = plotArea.getSpPr(); } else { properties = plotArea.addNewSpPr(); } return new XDDFShapeProperties(properties); } public void deleteShapeProperties() { if (getCTPlotArea().isSetSpPr()) { getCTPlotArea().unsetSpPr(); } } public XDDFChartLegend getOrAddLegend() { return new XDDFChartLegend(getCTChart()); } public void deleteLegend() { if (getCTChart().isSetLegend()) { getCTChart().unsetLegend(); } } public XDDFManualLayout getOrAddManualLayout() { return new XDDFManualLayout(getCTPlotArea()); } private long seriesCount = 0; protected long incrementSeriesCount() { return seriesCount++; } public void plot(XDDFChartData data) { XSSFSheet sheet = getSheet(); for (int idx = 0; idx < data.getSeriesCount(); idx++) { XDDFChartData.Series series = data.getSeries(idx); series.plot(); XDDFDataSource categoryDS = series.getCategoryData(); XDDFNumericalDataSource valuesDS = series.getValuesData(); if (categoryDS != null && !categoryDS.isCellRange() && !categoryDS.isLiteral() && valuesDS != null && !valuesDS.isCellRange() && !valuesDS.isLiteral()) { fillSheet(sheet, categoryDS, valuesDS); } // otherwise let's assume the data is already in the sheet } } public List getChartSeries() { List series = new LinkedList<>(); CTPlotArea plotArea = getCTPlotArea(); Map categories = getCategoryAxes(); Map values = getValueAxes(); for (int i = 0; i < plotArea.sizeOfAreaChartArray(); i++) { CTAreaChart areaChart = plotArea.getAreaChartArray(i); series.add(new XDDFAreaChartData(this, areaChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfArea3DChartArray(); i++) { CTArea3DChart areaChart = plotArea.getArea3DChartArray(i); series.add(new XDDFArea3DChartData(this, areaChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfBarChartArray(); i++) { CTBarChart barChart = plotArea.getBarChartArray(i); series.add(new XDDFBarChartData(this, barChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfBar3DChartArray(); i++) { CTBar3DChart barChart = plotArea.getBar3DChartArray(i); series.add(new XDDFBar3DChartData(this, barChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfDoughnutChartArray(); i++) { CTDoughnutChart doughnutChart = plotArea.getDoughnutChartArray(i); series.add(new XDDFDoughnutChartData(this, doughnutChart)); } for (int i = 0; i < plotArea.sizeOfLineChartArray(); i++) { CTLineChart lineChart = plotArea.getLineChartArray(i); series.add(new XDDFLineChartData(this, lineChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfLine3DChartArray(); i++) { CTLine3DChart lineChart = plotArea.getLine3DChartArray(i); series.add(new XDDFLine3DChartData(this, lineChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfPieChartArray(); i++) { CTPieChart pieChart = plotArea.getPieChartArray(i); series.add(new XDDFPieChartData(this, pieChart)); } for (int i = 0; i < plotArea.sizeOfPie3DChartArray(); i++) { CTPie3DChart pieChart = plotArea.getPie3DChartArray(i); series.add(new XDDFPie3DChartData(this, pieChart)); } for (int i = 0; i < plotArea.sizeOfRadarChartArray(); i++) { CTRadarChart radarChart = plotArea.getRadarChartArray(i); series.add(new XDDFRadarChartData(this, radarChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfScatterChartArray(); i++) { CTScatterChart scatterChart = plotArea.getScatterChartArray(i); series.add(new XDDFScatterChartData(this, scatterChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfSurfaceChartArray(); i++) { CTSurfaceChart surfaceChart = plotArea.getSurfaceChartArray(i); series.add(new XDDFSurfaceChartData(this, surfaceChart, categories, values)); } for (int i = 0; i < plotArea.sizeOfSurface3DChartArray(); i++) { CTSurface3DChart surfaceChart = plotArea.getSurface3DChartArray(i); series.add(new XDDFSurface3DChartData(this, surfaceChart, categories, values)); } // TODO repeat above code for missing charts: Bubble, OfPie and Stock seriesCount = series.size(); return series; } /** * Clear all chart series, as if a new instance had just been created. * @since POI 4.1.2 */ public void clearChartSeries() { CTPlotArea plotArea = getCTPlotArea(); for (int i = plotArea.sizeOfAreaChartArray(); i > 0; i--) { plotArea.removeAreaChart(i - 1); } for (int i = plotArea.sizeOfArea3DChartArray(); i > 0; i--) { plotArea.removeArea3DChart(i - 1); } for (int i = plotArea.sizeOfBarChartArray(); i > 0; i--) { plotArea.removeBarChart(i - 1); } for (int i = plotArea.sizeOfBar3DChartArray(); i > 0; i--) { plotArea.removeBar3DChart(i - 1); } for (int i = plotArea.sizeOfBubbleChartArray(); i > 0; i--) { plotArea.removeBubbleChart(i - 1); } for (int i = plotArea.sizeOfDoughnutChartArray(); i > 0; i--) { plotArea.removeDoughnutChart(i - 1); } for (int i = plotArea.sizeOfLineChartArray(); i > 0; i--) { plotArea.removeLineChart(i - 1); } for (int i = plotArea.sizeOfLine3DChartArray(); i > 0; i--) { plotArea.removeLine3DChart(i - 1); } for (int i = plotArea.sizeOfOfPieChartArray(); i > 0; i--) { plotArea.removeOfPieChart(i - 1); } for (int i = plotArea.sizeOfPieChartArray(); i > 0; i--) { plotArea.removePieChart(i - 1); } for (int i = plotArea.sizeOfPie3DChartArray(); i > 0; i--) { plotArea.removePie3DChart(i - 1); } for (int i = plotArea.sizeOfRadarChartArray(); i > 0; i--) { plotArea.removeRadarChart(i - 1); } for (int i = plotArea.sizeOfScatterChartArray(); i > 0; i--) { plotArea.removeScatterChart(i - 1); } for (int i = plotArea.sizeOfStockChartArray(); i > 0; i--) { plotArea.removeStockChart(i - 1); } for (int i = plotArea.sizeOfSurfaceChartArray(); i > 0; i--) { plotArea.removeSurfaceChart(i - 1); } for (int i = plotArea.sizeOfSurface3DChartArray(); i > 0; i--) { plotArea.removeSurface3DChart(i - 1); } } private Map getCategoryAxes() { CTPlotArea plotArea = getCTPlotArea(); int sizeOfArray = plotArea.sizeOfCatAxArray(); Map axesMap = new HashMap<>(sizeOfArray); for (int i = 0; i < sizeOfArray; i++) { CTCatAx category = plotArea.getCatAxArray(i); axesMap.put(category.getAxId().getVal(), new XDDFCategoryAxis(category)); } return axesMap; } private Map getValueAxes() { CTPlotArea plotArea = getCTPlotArea(); int sizeOfArray = plotArea.sizeOfValAxArray(); Map axesMap = new HashMap<>(sizeOfArray); for (int i = 0; i < sizeOfArray; i++) { CTValAx values = plotArea.getValAxArray(i); axesMap.put(values.getAxId().getVal(), new XDDFValueAxis(values)); } return axesMap; } public XDDFValueAxis createValueAxis(AxisPosition pos) { XDDFValueAxis valueAxis = new XDDFValueAxis(getCTPlotArea(), pos); addAxis(valueAxis); return valueAxis; } /** * this method will return series axis with specified position * * @param pos axis position Left, Right, Top, Bottom * @return series axis with specified position */ public XDDFSeriesAxis createSeriesAxis(AxisPosition pos) { XDDFSeriesAxis seriesAxis = new XDDFSeriesAxis(getCTPlotArea(), pos); addAxis(seriesAxis); return seriesAxis; } public XDDFCategoryAxis createCategoryAxis(AxisPosition pos) { XDDFCategoryAxis categoryAxis = new XDDFCategoryAxis(getCTPlotArea(), pos); addAxis(categoryAxis); return categoryAxis; } public XDDFDateAxis createDateAxis(AxisPosition pos) { XDDFDateAxis dateAxis = new XDDFDateAxis(getCTPlotArea(), pos); addAxis(dateAxis); return dateAxis; } private void addAxis(XDDFChartAxis newAxis) { if (axes.size() == 1) { XDDFChartAxis axis = axes.get(0); axis.crossAxis(newAxis); newAxis.crossAxis(axis); axis.setCrosses(AxisCrosses.AUTO_ZERO); newAxis.setCrosses(AxisCrosses.AUTO_ZERO); } axes.add(newAxis); } /** * this method will return specified chart data with category and series values * * @param type chart type * @param category category values of chart * @param values series values of chart * @return specified chart data. */ public XDDFChartData createData(ChartTypes type, XDDFChartAxis category, XDDFValueAxis values) { Map categories = null; Map mapValues = null; if (ChartTypes.PIE != type && ChartTypes.PIE3D != type && ChartTypes.DOUGHNUT != type) { categories = Collections.singletonMap(category.getId(), category); mapValues = Collections.singletonMap(values.getId(), values); } final CTPlotArea plotArea = getCTPlotArea(); switch (type) { case AREA: return new XDDFAreaChartData(this, plotArea.addNewAreaChart(), categories, mapValues); case AREA3D: return new XDDFArea3DChartData(this, plotArea.addNewArea3DChart(), categories, mapValues); case BAR: return new XDDFBarChartData(this, plotArea.addNewBarChart(), categories, mapValues); case BAR3D: return new XDDFBar3DChartData(this, plotArea.addNewBar3DChart(), categories, mapValues); case DOUGHNUT: return new XDDFDoughnutChartData(this, plotArea.addNewDoughnutChart()); case LINE: return new XDDFLineChartData(this, plotArea.addNewLineChart(), categories, mapValues); case LINE3D: return new XDDFLine3DChartData(this, plotArea.addNewLine3DChart(), categories, mapValues); case PIE: return new XDDFPieChartData(this, plotArea.addNewPieChart()); case PIE3D: return new XDDFPie3DChartData(this, plotArea.addNewPie3DChart()); case RADAR: return new XDDFRadarChartData(this, plotArea.addNewRadarChart(), categories, mapValues); case SCATTER: return new XDDFScatterChartData(this, plotArea.addNewScatterChart(), categories, mapValues); case SURFACE: return new XDDFSurfaceChartData(this, plotArea.addNewSurfaceChart(), categories, mapValues); case SURFACE3D: return new XDDFSurface3DChartData(this, plotArea.addNewSurface3DChart(), categories, mapValues); // TODO repeat above code for missing charts: Bubble, OfPie and Stock default: return null; } } public List getAxes() { if (axes.isEmpty() && hasAxes()) { parseAxes(); } return axes; } private boolean hasAxes() { CTPlotArea ctPlotArea = getCTPlotArea(); int totalAxisCount = ctPlotArea.sizeOfValAxArray() + ctPlotArea.sizeOfCatAxArray() + ctPlotArea .sizeOfDateAxArray() + ctPlotArea.sizeOfSerAxArray(); return totalAxisCount > 0; } private void parseAxes() { for (CTCatAx catAx : getCTPlotArea().getCatAxArray()) { axes.add(new XDDFCategoryAxis(catAx)); } for (CTDateAx dateAx : getCTPlotArea().getDateAxArray()) { axes.add(new XDDFDateAxis(dateAx)); } for (CTSerAx serAx : getCTPlotArea().getSerAxArray()) { axes.add(new XDDFSeriesAxis(serAx)); } for (CTValAx valAx : getCTPlotArea().getValAxArray()) { axes.add(new XDDFValueAxis(valAx)); } } /** * Set value range (basic Axis Options) * * @param axisIndex * 0 - primary axis, 1 - secondary axis * @param minimum * minimum value; Double.NaN - automatic; null - no change * @param maximum * maximum value; Double.NaN - automatic; null - no change * @param majorUnit * major unit value; Double.NaN - automatic; null - no change * @param minorUnit * minor unit value; Double.NaN - automatic; null - no change */ public void setValueRange(int axisIndex, Double minimum, Double maximum, Double majorUnit, Double minorUnit) { XDDFChartAxis axis = getAxes().get(axisIndex); if (axis == null) { return; } if (minimum != null) { axis.setMinimum(minimum); } if (maximum != null) { axis.setMaximum(maximum); } if (majorUnit != null) { axis.setMajorUnit(majorUnit); } if (minorUnit != null) { axis.setMinorUnit(minorUnit); } } /** * method to create relationship with embedded part for example writing xlsx * file stream into output stream * * @param chartRelation * relationship object * @param chartFactory * ChartFactory object * @param chartIndex * index used to suffix on file * @return return relation part which used to write relation in .rels file * and get relation id * @since POI 4.0.0 */ public PackageRelationship createRelationshipInChart(POIXMLRelation chartRelation, POIXMLFactory chartFactory, int chartIndex) { POIXMLDocumentPart documentPart = createRelationship(chartRelation, chartFactory, chartIndex, true).getDocumentPart(); return addRelation(null, chartRelation, documentPart).getRelationship(); } /** * if embedded part was null then create new part * * @param chartWorkbookRelation * chart workbook relation object * @param chartFactory * factory object of POIXMLFactory (XWPFFactory/XSLFFactory) * @return return the new package part * @since POI 4.0.0 */ private PackagePart createWorksheetPart(POIXMLRelation chartWorkbookRelation, POIXMLFactory chartFactory) throws InvalidFormatException { PackageRelationship xlsx = createRelationshipInChart(chartWorkbookRelation, chartFactory, chartIndex); setExternalId(xlsx.getId()); return getTargetPart(xlsx); } /** * this method write the XSSFWorkbook object data into embedded excel file * * @param workbook XSSFworkbook object * @since POI 4.0.0 */ public void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException { PackagePart worksheetPart = getWorksheetPart(); if (worksheetPart == null) { POIXMLRelation chartWorkbookRelation = getChartWorkbookRelation(); POIXMLFactory chartFactory = getChartFactory(); if (chartWorkbookRelation != null && chartFactory != null) { worksheetPart = createWorksheetPart(chartWorkbookRelation, chartFactory); } else { throw new InvalidFormatException("unable to determine chart relations"); } } try (OutputStream xlsOut = worksheetPart.getOutputStream()) { setWorksheetPartCommitted(); workbook.write(xlsOut); } } /** * * @return the chart relation in the implementing subclass. * @since POI 4.0.0 */ protected abstract POIXMLRelation getChartRelation(); /** * * @return the chart workbook relation in the implementing subclass. * @since POI 4.0.0 */ protected abstract POIXMLRelation getChartWorkbookRelation(); /** * * @return the chart factory in the implementing subclass. * @since POI 4.0.0 */ protected abstract POIXMLFactory getChartFactory(); /** * this method writes the data into sheet * * @param sheet * sheet of embedded excel * @param categoryData * category values * @param valuesData * data values * @since POI 4.0.0 */ protected void fillSheet(XSSFSheet sheet, XDDFDataSource categoryData, XDDFNumericalDataSource valuesData) { int numOfPoints = categoryData.getPointCount(); for (int i = 0; i < numOfPoints; i++) { XSSFRow row = getRow(sheet, i + 1); // first row is for title Object category = categoryData.getPointAt(i); if (category != null) { getCell(row, categoryData.getColIndex()).setCellValue(category.toString()); } Number value = valuesData.getPointAt(i); if (value != null) { getCell(row, valuesData.getColIndex()).setCellValue(value.doubleValue()); } } } /** * this method return row on given index if row is null then create new row * * @param sheet * current sheet object * @param index * index of current row * @return this method return sheet row on given index * @since POI 4.0.0 */ private XSSFRow getRow(XSSFSheet sheet, int index) { XSSFRow row = sheet.getRow(index); if (row == null) { return sheet.createRow(index); } else { return row; } } /** * this method return cell on given index if cell is null then create new * cell * * @param row * current row object * @param index * index of current cell * @return this method return sheet cell on given index * @since POI 4.0.0 */ private XSSFCell getCell(XSSFRow row, int index) { XSSFCell cell = row.getCell(index); if (cell == null) { return row.createCell(index); } else { return cell; } } /** * import content from other chart to created chart * * @param other * chart object * @since POI 4.0.0 */ public void importContent(XDDFChart other) { getCTChartSpace().set(other.getCTChartSpace()); } /** * save chart xml */ @Override protected void commit() throws IOException { XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); xmlOptions.setSaveSyntheticDocumentElement( new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); if (workbook != null) { try { saveWorkbook(workbook); } catch (InvalidFormatException e) { throw new POIXMLException(e); } } PackagePart part = getPackagePart(); try (OutputStream out = part.getOutputStream()) { chartSpace.save(out, xmlOptions); } } /** * set sheet title in excel file * * @param title * title of sheet * @param column * column index * @return return cell reference * @since POI 4.0.0 */ public CellReference setSheetTitle(String title, int column) { XSSFSheet sheet = getSheet(); if (sheet == null) { return null; } XSSFRow row = getRow(sheet, 0); XSSFCell cell = getCell(row, column); cell.setCellValue(title); return new CellReference(sheet.getSheetName(), 0, column, true, true); } /** * @since POI 4.0.0 */ public String formatRange(CellRangeAddress range) { final XSSFSheet sheet = getSheet(); return (sheet == null) ? null : range.formatAsString(sheet.getSheetName(), true); } /** * get sheet object of embedded excel file * * @return excel sheet object * @since POI 4.0.0 */ private XSSFSheet getSheet() { try { return getWorkbook().getSheetAt(0); } catch (InvalidFormatException | IOException ignored) { return null; } } /** * this method is used to get worksheet part if call is from saveworkbook * method then check isCommitted isCommitted variable shows that we are * writing xssfworkbook object into output stream of embedded part * * @return returns the packagepart of embedded file * @since POI 4.0.0 */ private PackagePart getWorksheetPart() throws InvalidFormatException { for (RelationPart part : getRelationParts()) { if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) { return getTargetPart(part.getRelationship()); } } return null; } private void setWorksheetPartCommitted() { for (RelationPart part : getRelationParts()) { if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) { part.getDocumentPart().setCommitted(true); break; } } } /** * @return returns the workbook object of embedded excel file * @since POI 4.0.0 */ public XSSFWorkbook getWorkbook() throws IOException, InvalidFormatException { if (workbook == null) { try { PackagePart worksheetPart = getWorksheetPart(); if (worksheetPart == null) { workbook = new XSSFWorkbook(); workbook.createSheet(); } else { workbook = new XSSFWorkbook(worksheetPart.getInputStream()); } } catch (NotOfficeXmlFileException e) { workbook = new XSSFWorkbook(); workbook.createSheet(); } } return workbook; } /** * while reading chart from template file then we need to parse and store * embedded excel file in chart object show that we can modify value * according to use * * @param workbook * workbook object which we read from chart embedded part * @since POI 4.0.0 */ public void setWorkbook(XSSFWorkbook workbook) { this.workbook = workbook; } /** * set the relation id of embedded excel relation id into external data * relation tag * * @param id * relation id of embedded excel relation id into external data * relation tag * @since POI 4.0.0 */ public void setExternalId(String id) { CTChartSpace ctChartSpace = getCTChartSpace(); CTExternalData externalData = ctChartSpace.isSetExternalData() ? ctChartSpace.getExternalData() : ctChartSpace.addNewExternalData(); externalData.setId(id); } /** * @return method return chart index * @since POI 4.0.0 */ protected int getChartIndex() { return chartIndex; } /** * Set chart index which can be used for relation part. * * @param chartIndex * 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.1.0 */ 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(); } } } }