From 3b8f806850882b1aa60923526b44950d215a8990 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alain=20B=C3=A9arez?= Date: Sun, 26 Nov 2017 14:03:01 +0000 Subject: [PATCH] XDDF usermodel for Charts closes #68 on GitHub git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1816383 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- .../poi/xslf/usermodel/BarChartDemo.java | 145 +++++ .../poi/xslf/usermodel/PieChartDemo.java | 137 ++--- .../poi/xslf/usermodel/bar-chart-data.txt | 4 + .../xslf/usermodel/bar-chart-template.pptx | Bin 0 -> 44410 bytes .../xssf/usermodel/examples/LineChart.java | 66 +-- .../xssf/usermodel/examples/ScatterChart.java | 72 +-- .../poi/hssf/usermodel/HSSFPatriarch.java | 9 - .../org/apache/poi/ss/usermodel/Chart.java | 13 +- .../org/apache/poi/ss/usermodel/Drawing.java | 14 +- .../ss/usermodel/charts/AxisCrossBetween.java | 6 +- .../poi/ss/usermodel/charts/AxisCrosses.java | 6 +- .../ss/usermodel/charts/AxisOrientation.java | 6 +- .../poi/ss/usermodel/charts/AxisPosition.java | 6 +- .../poi/ss/usermodel/charts/AxisTickMark.java | 6 +- .../poi/ss/usermodel/charts/ChartAxis.java | 9 +- .../ss/usermodel/charts/ChartAxisFactory.java | 16 +- .../poi/ss/usermodel/charts/ChartData.java | 7 +- .../ss/usermodel/charts/ChartDataFactory.java | 7 +- .../ss/usermodel/charts/ChartDataSource.java | 7 +- .../poi/ss/usermodel/charts/ChartLegend.java | 8 +- .../poi/ss/usermodel/charts/ChartSeries.java | 4 + .../poi/ss/usermodel/charts/DataSources.java | 13 +- .../poi/ss/usermodel/charts/LayoutMode.java | 6 +- .../poi/ss/usermodel/charts/LayoutTarget.java | 6 +- .../ss/usermodel/charts/LegendPosition.java | 6 +- .../ss/usermodel/charts/LineChartData.java | 7 +- .../ss/usermodel/charts/LineChartSeries.java | 7 +- .../poi/ss/usermodel/charts/ManualLayout.java | 9 +- .../charts/ManuallyPositionable.java | 7 +- .../ss/usermodel/charts/ScatterChartData.java | 6 + .../usermodel/charts/ScatterChartSeries.java | 7 +- .../poi/ss/usermodel/charts/TitleType.java | 6 +- .../poi/ss/usermodel/charts/ValueAxis.java | 7 +- .../usermodel/chart/AxisCrossBetween.java | 44 ++ .../poi/xddf/usermodel/chart/AxisCrosses.java | 45 ++ .../usermodel/chart/AxisLabelAlignment.java | 45 ++ .../xddf/usermodel/chart/AxisOrientation.java | 44 ++ .../xddf/usermodel/chart/AxisPosition.java | 46 ++ .../chart/AxisTickLabelPosition.java | 46 ++ .../xddf/usermodel/chart/AxisTickMark.java | 46 ++ .../xddf/usermodel/chart/BarDirection.java | 44 ++ .../poi/xddf/usermodel/chart/BarGrouping.java | 46 ++ .../poi/xddf/usermodel/chart/ChartTypes.java | 25 + .../poi/xddf/usermodel/chart/Grouping.java | 45 ++ .../poi/xddf/usermodel/chart/LayoutMode.java | 44 ++ .../xddf/usermodel/chart/LayoutTarget.java | 44 ++ .../xddf/usermodel/chart/LegendPosition.java | 47 ++ .../poi/xddf/usermodel/chart/MarkerStyle.java | 53 ++ .../poi/xddf/usermodel/chart/RadarStyle.java | 45 ++ .../xddf/usermodel/chart/ScatterStyle.java | 48 ++ .../usermodel/chart/XDDFBarChartData.java | 146 +++++ .../usermodel/chart/XDDFCategoryAxis.java | 151 +++++ .../chart/XDDFCategoryDataSource.java | 26 + .../poi/xddf/usermodel/chart/XDDFChart.java | 331 +++++++++++ .../xddf/usermodel/chart/XDDFChartAxis.java | 302 ++++++++++ .../xddf/usermodel/chart/XDDFChartData.java | 291 +++++++++ .../chart/XDDFChartExtensionList.java | 41 ++ .../xddf/usermodel/chart/XDDFChartLegend.java | 184 ++++++ .../xddf/usermodel/chart/XDDFDataSource.java | 35 ++ .../chart/XDDFDataSourcesFactory.java | 299 ++++++++++ .../xddf/usermodel/chart/XDDFDateAxis.java | 147 +++++ .../poi/xddf/usermodel/chart/XDDFLayout.java | 73 +++ .../xddf/usermodel/chart/XDDFLegendEntry.java | 98 +++ .../usermodel/chart/XDDFLineChartData.java | 141 +++++ .../usermodel/chart/XDDFManualLayout.java | 221 +++++++ .../chart/XDDFNumericalDataSource.java | 29 + .../usermodel/chart/XDDFPieChartData.java | 118 ++++ .../usermodel/chart/XDDFRadarChartData.java | 119 ++++ .../usermodel/chart/XDDFScatterChartData.java | 123 ++++ .../xddf/usermodel/chart/XDDFValueAxis.java | 153 +++++ .../poi/xddf/usermodel/text/XDDFTextBody.java | 41 ++ .../poi/xslf/usermodel/XMLSlideShow.java | 146 +++-- .../apache/poi/xslf/usermodel/XSLFChart.java | 213 +++++-- .../poi/xslf/usermodel/XSLFGraphicFrame.java | 46 +- .../apache/poi/xslf/usermodel/XSLFSheet.java | 86 ++- .../apache/poi/xslf/usermodel/XSLFSlide.java | 100 ++-- .../poi/xssf/streaming/SXSSFDrawing.java | 6 - .../apache/poi/xssf/usermodel/XSSFChart.java | 561 +++++++++--------- .../poi/xssf/usermodel/XSSFDrawing.java | 15 +- .../charts/AbstractXSSFChartSeries.java | 11 + .../usermodel/charts/XSSFCategoryAxis.java | 12 +- .../xssf/usermodel/charts/XSSFChartAxis.java | 10 +- .../charts/XSSFChartDataFactory.java | 15 +- .../usermodel/charts/XSSFChartLegend.java | 26 +- .../xssf/usermodel/charts/XSSFChartUtil.java | 14 +- .../xssf/usermodel/charts/XSSFDateAxis.java | 10 +- .../usermodel/charts/XSSFLineChartData.java | 15 +- .../usermodel/charts/XSSFManualLayout.java | 72 ++- .../charts/XSSFScatterChartData.java | 15 +- .../xssf/usermodel/charts/XSSFValueAxis.java | 8 +- .../apache/poi/xwpf/usermodel/XWPFChart.java | 58 +- .../poi/xwpf/usermodel/XWPFDocument.java | 65 +- .../chart/TestXDDFDataSourcesFactory.java | 134 +++++ .../poi/xslf/usermodel/TestXSLFChart.java | 360 ++++++++--- .../poi/xssf/usermodel/TestXSSFChart.java | 29 +- .../charts/TestXSSFCategoryAxis.java | 20 +- .../usermodel/charts/TestXSSFChartAxis.java | 31 +- .../usermodel/charts/TestXSSFChartLegend.java | 51 +- .../usermodel/charts/TestXSSFChartTitle.java | 84 ++- .../usermodel/charts/TestXSSFDateAxis.java | 20 +- .../charts/TestXSSFLineChartData.java | 48 +- .../charts/TestXSSFManualLayout.java | 37 +- .../charts/TestXSSFScatterChartData.java | 50 +- .../usermodel/charts/TestXSSFValueAxis.java | 21 +- .../poi/xwpf/usermodel/TestXWPFChart.java | 18 +- test-data/slideshow/bar-chart.pptx | Bin 0 -> 44410 bytes test-data/slideshow/line-chart.pptx | Bin 0 -> 44700 bytes test-data/slideshow/radar-chart.pptx | Bin 0 -> 43689 bytes test-data/slideshow/scatter-chart.pptx | Bin 0 -> 44269 bytes 110 files changed, 5727 insertions(+), 1143 deletions(-) create mode 100644 src/examples/src/org/apache/poi/xslf/usermodel/BarChartDemo.java create mode 100644 src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-data.txt create mode 100644 src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-template.pptx create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrossBetween.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrosses.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisLabelAlignment.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisOrientation.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisPosition.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickLabelPosition.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickMark.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarDirection.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarGrouping.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ChartTypes.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/Grouping.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutMode.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutTarget.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LegendPosition.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/MarkerStyle.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/RadarStyle.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ScatterStyle.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFBarChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryAxis.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartAxis.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartExtensionList.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartLegend.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDateAxis.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLayout.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLegendEntry.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLineChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFManualLayout.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFNumericalDataSource.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFPieChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFRadarChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFScatterChartData.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFValueAxis.java create mode 100644 src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java create mode 100644 src/ooxml/testcases/org/apache/poi/xddf/usermodel/chart/TestXDDFDataSourcesFactory.java create mode 100644 test-data/slideshow/bar-chart.pptx create mode 100644 test-data/slideshow/line-chart.pptx create mode 100644 test-data/slideshow/radar-chart.pptx create mode 100644 test-data/slideshow/scatter-chart.pptx diff --git a/build.xml b/build.xml index a9ac116704..092d07c5f9 100644 --- a/build.xml +++ b/build.xml @@ -2051,7 +2051,7 @@ under the License. - + diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/BarChartDemo.java b/src/examples/src/org/apache/poi/xslf/usermodel/BarChartDemo.java new file mode 100644 index 0000000000..c66b9e00ed --- /dev/null +++ b/src/examples/src/org/apache/poi/xslf/usermodel/BarChartDemo.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * 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.xslf.usermodel; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisOrientation; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.BarDirection; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; + +/** + * Build a bar chart from a template pptx + */ +public class BarChartDemo { + private static void usage(){ + System.out.println("Usage: BarChartDemo "); + System.out.println(" bar-chart-template.pptx template with a bar chart"); + System.out.println(" bar-chart-data.txt the model to set. First line is chart title, " + + "then go pairs {axis-label value}"); + } + + public static void main(String[] args) throws Exception { + if(args.length < 2) { + usage(); + return; + } + + BufferedReader modelReader = new BufferedReader(new FileReader(args[1])); + XMLSlideShow pptx = null; + try { + String chartTitle = modelReader.readLine(); // first line is chart title + + // Category Axis Data + List listCategories = new ArrayList(3); + + // Values + List listValues = new ArrayList(3); + + // set model + String ln; + while((ln = modelReader.readLine()) != null){ + String[] vals = ln.split("\\s+"); + listCategories.add(vals[0]); + listValues.add(Double.valueOf(vals[1])); + } + String[] categories = listCategories.toArray(new String[listCategories.size()]); + Double[] values = listValues.toArray(new Double[listValues.size()]); + + pptx = new XMLSlideShow(new FileInputStream(args[0])); + XSLFSlide slide = pptx.getSlides().get(0); + setBarData(findChart(slide), chartTitle, categories, values); + + XSLFChart chart = findChart(pptx.createSlide().importContent(slide)); + setColumnData(chart, "Column variant"); + + // save the result + OutputStream out = new FileOutputStream("bar-chart-demo-output.pptx"); + try { + pptx.write(out); + } finally { + out.close(); + } + } finally { + if (pptx != null) { + pptx.close(); + } + modelReader.close(); + } + } + + private static void setBarData(XSLFChart chart, String chartTitle, String[] categories, Double[] values) { + final List series = chart.getChartSeries(); + final XDDFBarChartData bar = (XDDFBarChartData) series.get(0); + + final int numOfPoints = categories.length; + final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); + final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); + final XDDFDataSource categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange); + final XDDFNumericalDataSource valuesData = XDDFDataSourcesFactory.fromArray(values, valuesDataRange); + + bar.getSeries().get(0).replaceData(categoriesData, valuesData); + bar.getSeries().get(0).setTitle(chartTitle, chart.setSheetTitle(chartTitle)); + chart.plot(bar); + } + + private static void setColumnData(XSLFChart chart, String chartTitle) { + // Series Text + List series = chart.getChartSeries(); + XDDFBarChartData bar = (XDDFBarChartData) series.get(0); + bar.getSeries().get(0).setTitle(chartTitle, chart.setSheetTitle(chartTitle)); + + // in order to transform a bar chart into a column chart, you just need to change the bar direction + bar.setBarDirection(BarDirection.COL); + + // additionally, you can adjust the axes + bar.getCategoryAxis().setOrientation(AxisOrientation.MAX_MIN); + bar.getValueAxes().get(0).setPosition(AxisPosition.TOP); + } + + private static XSLFChart findChart(XSLFSlide slide) { + // find chart in the slide + XSLFChart chart = null; + for(POIXMLDocumentPart part : slide.getRelations()){ + if(part instanceof XSLFChart){ + chart = (XSLFChart) part; + break; + } + } + + if(chart == null) { + throw new IllegalStateException("chart not found in the template"); + } + return chart; + } +} diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java b/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java index 9ba7461915..45122f7b2f 100644 --- a/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java +++ b/src/examples/src/org/apache/poi/xslf/usermodel/PieChartDemo.java @@ -19,34 +19,24 @@ package org.apache.poi.xslf.usermodel; -import org.apache.poi.POIXMLDocumentPart; -import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellReference; -import org.apache.poi.xssf.usermodel.XSSFRow; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrVal; - import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData; /** * Build a pie chart from a template pptx - * - * @author Yegor Kozlov */ public class PieChartDemo { private static void usage(){ @@ -77,74 +67,45 @@ public class PieChartDemo { } } - if (chart == null) throw new IllegalStateException("chart not found in the template"); - - // embedded Excel workbook that holds the chart data - POIXMLDocumentPart xlsPart = chart.getRelations().get(0); - try (XSSFWorkbook wb = new XSSFWorkbook()) { - XSSFSheet sheet = wb.createSheet(); - - CTChart ctChart = chart.getCTChart(); - CTPlotArea plotArea = ctChart.getPlotArea(); - - CTPieChart pieChart = plotArea.getPieChartArray(0); - //Pie Chart Series - CTPieSer ser = pieChart.getSerArray(0); - - // Series Text - CTSerTx tx = ser.getTx(); - tx.getStrRef().getStrCache().getPtArray(0).setV(chartTitle); - sheet.createRow(0).createCell(1).setCellValue(chartTitle); - String titleRef = new CellReference(sheet.getSheetName(), 0, 1, true, true).formatAsString(); - tx.getStrRef().setF(titleRef); - - // Category Axis Data - CTAxDataSource cat = ser.getCat(); - CTStrData strData = cat.getStrRef().getStrCache(); - - // Values - CTNumDataSource val = ser.getVal(); - CTNumData numData = val.getNumRef().getNumCache(); - - strData.setPtArray(null); // unset old axis text - numData.setPtArray(null); // unset old values - - // set model - int idx = 0; - int rownum = 1; - String ln; - while ((ln = modelReader.readLine()) != null) { - String[] vals = ln.split("\\s+"); - CTNumVal numVal = numData.addNewPt(); - numVal.setIdx(idx); - numVal.setV(vals[1]); - - CTStrVal sVal = strData.addNewPt(); - sVal.setIdx(idx); - sVal.setV(vals[0]); - - idx++; - XSSFRow row = sheet.createRow(rownum++); - row.createCell(0).setCellValue(vals[0]); - row.createCell(1).setCellValue(Double.valueOf(vals[1])); - } - numData.getPtCount().setVal(idx); - strData.getPtCount().setVal(idx); - - String numDataRange = new CellRangeAddress(1, rownum - 1, 1, 1).formatAsString(sheet.getSheetName(), true); - val.getNumRef().setF(numDataRange); - String axisDataRange = new CellRangeAddress(1, rownum - 1, 0, 0).formatAsString(sheet.getSheetName(), true); - cat.getStrRef().setF(axisDataRange); - - // updated the embedded workbook with the data - try (OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream()) { - wb.write(xlsOut); - } - - // save the result - try (OutputStream out = new FileOutputStream("pie-chart-demo-output.pptx")) { - pptx.write(out); - } + if(chart == null) { + throw new IllegalStateException("chart not found in the template"); + } + + // Series Text + List series = chart.getChartSeries(); + XDDFPieChartData pie = (XDDFPieChartData) series.get(0); + + // Category Axis Data + List listCategories = new ArrayList(3); + + // Values + List listValues = new ArrayList(3); + + // set model + String ln; + while((ln = modelReader.readLine()) != null){ + String[] vals = ln.split("\\s+"); + listCategories.add(vals[0]); + listValues.add(Double.valueOf(vals[1])); + } + String[] categories = listCategories.toArray(new String[listCategories.size()]); + Double[] values = listValues.toArray(new Double[listValues.size()]); + + final int numOfPoints = categories.length; + final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); + final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); + final XDDFDataSource categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange); + final XDDFNumericalDataSource valuesData = XDDFDataSourcesFactory.fromArray(values, valuesDataRange); + + XDDFPieChartData.Series firstSeries = (XDDFPieChartData.Series) pie.getSeries().get(0); + firstSeries.replaceData(categoriesData, valuesData); + firstSeries.setTitle(chartTitle, chart.setSheetTitle(chartTitle)); + firstSeries.setExplosion(25); + chart.plot(pie); + + // save the result + try (OutputStream out = new FileOutputStream("pie-chart-demo-output.pptx")) { + pptx.write(out); } } } diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-data.txt b/src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-data.txt new file mode 100644 index 0000000000..7f9c271036 --- /dev/null +++ b/src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-data.txt @@ -0,0 +1,4 @@ +My Bar or Column Chart +First 1.0 +Second 3.0 +Third 4.0 \ No newline at end of file diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-template.pptx b/src/examples/src/org/apache/poi/xslf/usermodel/bar-chart-template.pptx new file mode 100644 index 0000000000000000000000000000000000000000..e4d2613046ab69e2d0a5c529b41cbcaa49ac4e30 GIT binary patch literal 44410 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 drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); - - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); + + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); - - LineChartData data = chart.getChartDataFactory().createLineChartData(); - + // Use a category axis for the bottom axis. - ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); - ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); - - ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); - - + + XDDFDataSource xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); + + XDDFChartData data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis); data.addSeries(xs, ys1); data.addSeries(xs, ys2); - - chart.plot(data, bottomAxis, leftAxis); + chart.plot(data); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) { diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/ScatterChart.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/ScatterChart.java index ccd23cf9a7..6ad9dbaeaa 100644 --- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/ScatterChart.java +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/ScatterChart.java @@ -23,21 +23,22 @@ import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.AxisCrosses; -import org.apache.poi.ss.usermodel.charts.AxisPosition; -import org.apache.poi.ss.usermodel.charts.ChartDataSource; -import org.apache.poi.ss.usermodel.charts.ChartLegend; -import org.apache.poi.ss.usermodel.charts.DataSources; -import org.apache.poi.ss.usermodel.charts.LegendPosition; -import org.apache.poi.ss.usermodel.charts.ScatterChartData; -import org.apache.poi.ss.usermodel.charts.ValueAxis; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** @@ -46,8 +47,8 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class ScatterChart { public static void main(String[] args) throws IOException { - try (Workbook wb = new XSSFWorkbook()) { - Sheet sheet = wb.createSheet("Sheet 1"); + try (XSSFWorkbook wb = new XSSFWorkbook()) { + XSSFSheet sheet = wb.createSheet("Sheet 1"); final int NUM_OF_ROWS = 3; final int NUM_OF_COLUMNS = 10; @@ -61,29 +62,28 @@ public class ScatterChart { cell.setCellValue(colIndex * (rowIndex + 1)); } } - - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); - - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); + + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); - - ScatterChartData data = chart.getChartDataFactory().createScatterChartData(); - - ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); - ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + + XDDFValueAxis bottomAxis = chart.createValueAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); - - ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); - - - data.addSerie(xs, ys1); - data.addSerie(xs, ys2); - - chart.plot(data, bottomAxis, leftAxis); + + XDDFDataSource xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); + + + XDDFChartData data = chart.createData(ChartTypes.SCATTER, bottomAxis, leftAxis); + + data.addSeries(xs, ys1); + data.addSeries(xs, ys2); + chart.plot(data); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("ooxml-scatter-chart.xlsx")) { diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java index bc210b9cfe..c0aa36f524 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -45,13 +45,11 @@ import org.apache.poi.hssf.record.ObjRecord; import org.apache.poi.ss.util.CellReference; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryNode; -import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.util.HexDump; import org.apache.poi.util.Internal; -import org.apache.poi.util.NotImplemented; import org.apache.poi.util.StringUtil; /** @@ -523,13 +521,6 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing extends ShapeContainer { */ Comment createCellComment(ClientAnchor anchor); - /** - * Creates a chart. - * @param anchor the client anchor describes how this chart is attached to - * the sheet. - * @return the newly created chart - */ - Chart createChart(ClientAnchor anchor); - /** * Creates a new client anchor and sets the top-left and bottom-right * coordinates of the anchor. @@ -62,10 +54,10 @@ public interface Drawing extends ShapeContainer { * @return the newly created client anchor */ ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); - + /** - * Adds a new OLE Package Shape - * + * Adds a new OLE Package Shape + * * @param anchor the client anchor describes how this picture is * attached to the sheet. * @param storageId the storageId returned by {@link Workbook#addOlePackage(byte[], String, String, String)} diff --git a/src/java/org/apache/poi/ss/usermodel/charts/AxisCrossBetween.java b/src/java/org/apache/poi/ss/usermodel/charts/AxisCrossBetween.java index d470cf61f1..fa6d46dae9 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/AxisCrossBetween.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/AxisCrossBetween.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Specifies the possible crossing states of an axis. * - * @author Roman Kashitsyn + * @deprecated use XDDF AxisCrossBetween */ +@Deprecated +@Removal(version="4.2") public enum AxisCrossBetween { /** * Specifies the value axis shall cross the category axis diff --git a/src/java/org/apache/poi/ss/usermodel/charts/AxisCrosses.java b/src/java/org/apache/poi/ss/usermodel/charts/AxisCrosses.java index 906198bb40..29389de7ed 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/AxisCrosses.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/AxisCrosses.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Specifies the possible crossing points for an axis. * - * @author Roman Kashitsyn + * @deprecated use XDDF AxisCrosses instead */ +@Deprecated +@Removal(version="4.2") public enum AxisCrosses { /** * The category axis crosses at the zero point of the value axis (if diff --git a/src/java/org/apache/poi/ss/usermodel/charts/AxisOrientation.java b/src/java/org/apache/poi/ss/usermodel/charts/AxisOrientation.java index e8c219b437..22a408682b 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/AxisOrientation.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/AxisOrientation.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Specifies the possible ways to place a picture on a data point, series, wall, or floor. * - * @author Roman Kashitsyn + * @deprecated use XDDF AxisOrientation */ +@Deprecated +@Removal(version="4.2") public enum AxisOrientation { /** * Specifies that the values on the axis shall be reversed diff --git a/src/java/org/apache/poi/ss/usermodel/charts/AxisPosition.java b/src/java/org/apache/poi/ss/usermodel/charts/AxisPosition.java index db189f9983..e774d163eb 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/AxisPosition.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/AxisPosition.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Enumeration of all possible axis positions. * - * @author Roman Kashitsyn + * @deprecated use XDDF AxisPosition instead */ +@Deprecated +@Removal(version="4.2") public enum AxisPosition { BOTTOM, LEFT, diff --git a/src/java/org/apache/poi/ss/usermodel/charts/AxisTickMark.java b/src/java/org/apache/poi/ss/usermodel/charts/AxisTickMark.java index d25a00ad8c..80c6e7aaf8 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/AxisTickMark.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/AxisTickMark.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Enumeration of possible axis tick marks. * - * @author Martin Andersson + * @deprecated use XDDF AxisTickMark instead */ +@Deprecated +@Removal(version="4.2") public enum AxisTickMark { NONE, CROSS, diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartAxis.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartAxis.java index 45cec602f1..4d297541cd 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartAxis.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartAxis.java @@ -17,14 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * High level representation of chart axis. * - * @author Roman Kashitsyn + * @deprecated use XDDFChartAxis instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartAxis { /** @@ -153,7 +154,7 @@ public interface ChartAxis { * @param tickMark minor tick mark type. */ void setMinorTickMark(AxisTickMark tickMark); - + /** * Use this to check before retrieving a number format, as calling {@link #getNumberFormat()} may create a default one if none exists. * @return true if a number format element is defined, false if not diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartAxisFactory.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartAxisFactory.java index 3eb80e28b5..a66416003a 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartAxisFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartAxisFactory.java @@ -17,31 +17,31 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * A factory for different chart axis. * - * @author Roman Kashitsyn - * @author Martin Andersson + * @deprecated */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartAxisFactory { /** - * @param pos + * @param pos * @return new value axis at the end of the list at the specified chart position */ ValueAxis createValueAxis(AxisPosition pos); /** - * @param pos + * @param pos * @return new category axis at the end of the list at the specified chart position */ ChartAxis createCategoryAxis(AxisPosition pos); - + /** - * @param pos + * @param pos * @return new date category axis at the end of the list at the specified chart position */ ChartAxis createDateAxis(AxisPosition pos); diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartData.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartData.java index 7a4aec5f0b..5048d71d34 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartData.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartData.java @@ -18,14 +18,15 @@ package org.apache.poi.ss.usermodel.charts; import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * A base for all charts data types. * - * @author Roman Kashitsyn + * @deprecated use XDDFChartData instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartData { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartDataFactory.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartDataFactory.java index 675322da70..3e053e3817 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartDataFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartDataFactory.java @@ -17,14 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * A factory for different charts data types. * - * @author Roman Kashitsyn, Martin Andersson + * @deprecated */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartDataFactory { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartDataSource.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartDataSource.java index d843cac9b8..a451ceb439 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartDataSource.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartDataSource.java @@ -18,15 +18,16 @@ */ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Represents data model of the charts. * * @param type of points the data source contents - * @author Roman Kashitsyn + * @deprecated use XDDFDataSource instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartDataSource { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartLegend.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartLegend.java index f8ab5c9695..629b9bf146 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartLegend.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartLegend.java @@ -17,15 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * High level representation of chart legend. * - * @author Roman Kashitsyn - * @author Martin Andersson + * @deprecated use XDDFChartLegend instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ChartLegend extends ManuallyPositionable { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ChartSeries.java b/src/java/org/apache/poi/ss/usermodel/charts/ChartSeries.java index 2ad666a8b8..bfebe94802 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ChartSeries.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ChartSeries.java @@ -18,10 +18,14 @@ package org.apache.poi.ss.usermodel.charts; import org.apache.poi.ss.util.CellReference; +import org.apache.poi.util.Removal; /** * Basic settings for all chart series. + * @deprecated */ +@Deprecated +@Removal(version="4.2") public interface ChartSeries { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/DataSources.java b/src/java/org/apache/poi/ss/usermodel/charts/DataSources.java index 5ebb8cf585..b5c5e7849f 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/DataSources.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/DataSources.java @@ -19,16 +19,21 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.CellValue; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Class {@code DataSources} is a factory for {@link ChartDataSource} instances. * - * @author Roman Kashitsyn + *@deprecated use XDDFDataSourcesFactory instead */ -@Beta +@Deprecated +@Removal(version="4.2") public class DataSources { private DataSources() { diff --git a/src/java/org/apache/poi/ss/usermodel/charts/LayoutMode.java b/src/java/org/apache/poi/ss/usermodel/charts/LayoutMode.java index 794c774887..22bbd8d2a9 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/LayoutMode.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/LayoutMode.java @@ -17,10 +17,14 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Specifies the possible ways to store a chart element's position. - * @author Roman Kashitsyn + * @use XDDF LayoutMode instead */ +@Deprecated +@Removal(version="4.2") public enum LayoutMode { /** * Specifies that the Width or Height shall be interpreted as the diff --git a/src/java/org/apache/poi/ss/usermodel/charts/LayoutTarget.java b/src/java/org/apache/poi/ss/usermodel/charts/LayoutTarget.java index 2a8b110cb1..73c7754fc4 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/LayoutTarget.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/LayoutTarget.java @@ -17,12 +17,16 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Specifies whether to layout the plot area by its inside (not including axis * and axis labels) or outside (including axis and axis labels). * - * @author Roman Kashitsyn + * @deprecated use XDDF LayoutTarget instead */ +@Deprecated +@Removal(version="4.2") public enum LayoutTarget { /** * Specifies that the plot area size shall determine the diff --git a/src/java/org/apache/poi/ss/usermodel/charts/LegendPosition.java b/src/java/org/apache/poi/ss/usermodel/charts/LegendPosition.java index 7536168a6b..826eff2f93 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/LegendPosition.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/LegendPosition.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Enumeration of all possible chart legend positions. * - * @author Roman Kashitsyn + * @deprecated use XDDF LayoutPosition instead */ +@Deprecated +@Removal(version="4.2") public enum LegendPosition { BOTTOM, LEFT, diff --git a/src/java/org/apache/poi/ss/usermodel/charts/LineChartData.java b/src/java/org/apache/poi/ss/usermodel/charts/LineChartData.java index 4fa9f8e6fa..39c4674b7e 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/LineChartData.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/LineChartData.java @@ -19,12 +19,15 @@ package org.apache.poi.ss.usermodel.charts; import java.util.List; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Data for a Line Chart + * + * @deprecated use XDDFLineChartData instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface LineChartData extends ChartData { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/LineChartSeries.java b/src/java/org/apache/poi/ss/usermodel/charts/LineChartSeries.java index 842c442a5c..8675b77de3 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/LineChartSeries.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/LineChartSeries.java @@ -17,12 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Represents a line chart series. + * + * @deprecated use XDDFLineChartData.Series instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface LineChartSeries extends ChartSeries { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ManualLayout.java b/src/java/org/apache/poi/ss/usermodel/charts/ManualLayout.java index cd78c1c95a..8b63276151 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ManualLayout.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ManualLayout.java @@ -17,14 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * High level representation of chart element manual layout. * - * @author Roman Kashitsyn + * @deprecated use XDDFManualLayout instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ManualLayout { /** @@ -117,7 +118,7 @@ public interface ManualLayout { public void setHeightMode(LayoutMode mode); /** - * Returns current height mode of this + * Returns current height mode of this * @return height mode of this manual layout. */ public LayoutMode getHeightMode(); diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ManuallyPositionable.java b/src/java/org/apache/poi/ss/usermodel/charts/ManuallyPositionable.java index 3fc5ba1fb6..ee4abad22c 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ManuallyPositionable.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ManuallyPositionable.java @@ -17,15 +17,16 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Abstraction of chart element that can be positioned with manual * layout. * - * @author Roman Kashitsyn + * @deprecated */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ManuallyPositionable { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartData.java b/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartData.java index 594b5bdfe3..ae178c747d 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartData.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartData.java @@ -19,9 +19,15 @@ package org.apache.poi.ss.usermodel.charts; import java.util.List; +import org.apache.poi.util.Removal; + /** * Data for a Scatter Chart + * + * @deprecated use XDDFScatterChartData instead */ +@Deprecated +@Removal(version="4.2") public interface ScatterChartData extends ChartData { /** * @param xs data source to be used for X axis values diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartSeries.java b/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartSeries.java index f200cb6ba2..6967f7ffbf 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartSeries.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ScatterChartSeries.java @@ -17,12 +17,15 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** * Represents scatter charts series. + * + * @deprecated use XDFFScatterChartData.Series instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ScatterChartSeries extends ChartSeries { /** diff --git a/src/java/org/apache/poi/ss/usermodel/charts/TitleType.java b/src/java/org/apache/poi/ss/usermodel/charts/TitleType.java index 07b5beb8a3..475adf06bc 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/TitleType.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/TitleType.java @@ -17,11 +17,15 @@ package org.apache.poi.ss.usermodel.charts; +import org.apache.poi.util.Removal; + /** * Title types for charts. * - * @author Martin Andersson + * @deprecated */ +@Deprecated +@Removal(version="4.2") public enum TitleType { STRING, CELL_REFERENCE diff --git a/src/java/org/apache/poi/ss/usermodel/charts/ValueAxis.java b/src/java/org/apache/poi/ss/usermodel/charts/ValueAxis.java index f6673dae47..d16ff52b06 100644 --- a/src/java/org/apache/poi/ss/usermodel/charts/ValueAxis.java +++ b/src/java/org/apache/poi/ss/usermodel/charts/ValueAxis.java @@ -17,12 +17,13 @@ package org.apache.poi.ss.usermodel.charts; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; /** - * @author Roman Kashitsyn + * @deprecated use XDDFValueAxis instead */ -@Beta +@Deprecated +@Removal(version="4.2") public interface ValueAxis extends ChartAxis { /** diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrossBetween.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrossBetween.java new file mode 100644 index 0000000000..ec2fc32a44 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrossBetween.java @@ -0,0 +1,44 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STCrossBetween; + +public enum AxisCrossBetween { + BETWEEN(STCrossBetween.BETWEEN), + MIDPOINT_CATEGORY(STCrossBetween.MID_CAT); + + final STCrossBetween.Enum underlying; + + AxisCrossBetween(STCrossBetween.Enum crossBetween) { + this.underlying = crossBetween; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisCrossBetween value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisCrossBetween valueOf(STCrossBetween.Enum crossBetween) { + return reverse.get(crossBetween); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrosses.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrosses.java new file mode 100644 index 0000000000..ff9d32dbb0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisCrosses.java @@ -0,0 +1,45 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STCrosses; + +public enum AxisCrosses { + AUTO_ZERO(STCrosses.AUTO_ZERO), + MAX(STCrosses.MAX), + MIN(STCrosses.MIN); + + final STCrosses.Enum underlying; + + AxisCrosses(STCrosses.Enum crosses) { + this.underlying = crosses; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisCrosses value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisCrosses valueOf(STCrosses.Enum crosses) { + return reverse.get(crosses); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisLabelAlignment.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisLabelAlignment.java new file mode 100644 index 0000000000..dff809cd36 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisLabelAlignment.java @@ -0,0 +1,45 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STLblAlgn; + +public enum AxisLabelAlignment { + CENTER(STLblAlgn.CTR), + LEFT(STLblAlgn.L), + RIGHT(STLblAlgn.R); + + final STLblAlgn.Enum underlying; + + AxisLabelAlignment(STLblAlgn.Enum alignment) { + this.underlying = alignment; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisLabelAlignment value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisLabelAlignment valueOf(STLblAlgn.Enum alignment) { + return reverse.get(alignment); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisOrientation.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisOrientation.java new file mode 100644 index 0000000000..ce0ace3fac --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisOrientation.java @@ -0,0 +1,44 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STOrientation; + +public enum AxisOrientation { + MIN_MAX(STOrientation.MIN_MAX), + MAX_MIN(STOrientation.MAX_MIN); + + final STOrientation.Enum underlying; + + AxisOrientation(STOrientation.Enum orientation) { + this.underlying = orientation; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisOrientation value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisOrientation valueOf(STOrientation.Enum orientation) { + return reverse.get(orientation); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisPosition.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisPosition.java new file mode 100644 index 0000000000..de277391ec --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisPosition.java @@ -0,0 +1,46 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STAxPos; + +public enum AxisPosition { + BOTTOM(STAxPos.B), + LEFT(STAxPos.L), + RIGHT(STAxPos.R), + TOP(STAxPos.T); + + final STAxPos.Enum underlying; + + AxisPosition(STAxPos.Enum position) { + this.underlying = position; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisPosition value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisPosition valueOf(STAxPos.Enum position) { + return reverse.get(position); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickLabelPosition.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickLabelPosition.java new file mode 100644 index 0000000000..5e22d2f283 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickLabelPosition.java @@ -0,0 +1,46 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos; + +public enum AxisTickLabelPosition { + HIGH(STTickLblPos.HIGH), + LOW(STTickLblPos.LOW), + NEXT_TO(STTickLblPos.NEXT_TO), + NONE(STTickLblPos.NONE); + + final STTickLblPos.Enum underlying; + + AxisTickLabelPosition(STTickLblPos.Enum position) { + this.underlying = position; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisTickLabelPosition value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisTickLabelPosition valueOf(STTickLblPos.Enum position) { + return reverse.get(position); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickMark.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickMark.java new file mode 100644 index 0000000000..f26cf5634c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/AxisTickMark.java @@ -0,0 +1,46 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STTickMark; + +public enum AxisTickMark { + CROSS(STTickMark.CROSS), + IN(STTickMark.IN), + NONE(STTickMark.NONE), + OUT(STTickMark.OUT); + + final STTickMark.Enum underlying; + + AxisTickMark(STTickMark.Enum tickMark) { + this.underlying = tickMark; + } + + private final static HashMap reverse = new HashMap(); + static { + for (AxisTickMark value : values()) { + reverse.put(value.underlying, value); + } + } + + static AxisTickMark valueOf(STTickMark.Enum tickMark) { + return reverse.get(tickMark); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarDirection.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarDirection.java new file mode 100644 index 0000000000..027196795f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarDirection.java @@ -0,0 +1,44 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STBarDir; + +public enum BarDirection { + BAR(STBarDir.BAR), + COL(STBarDir.COL); + + final STBarDir.Enum underlying; + + BarDirection(STBarDir.Enum direction) { + this.underlying = direction; + } + + private final static HashMap reverse = new HashMap(); + static { + for (BarDirection value : values()) { + reverse.put(value.underlying, value); + } + } + + static BarDirection valueOf(STBarDir.Enum direction) { + return reverse.get(direction); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarGrouping.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarGrouping.java new file mode 100644 index 0000000000..ce8615eb1d --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/BarGrouping.java @@ -0,0 +1,46 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STBarGrouping; + +public enum BarGrouping { + STANDARD(STBarGrouping.STANDARD), + CLUSTERED(STBarGrouping.CLUSTERED), + STACKED(STBarGrouping.STACKED), + PERCENT_STACKED(STBarGrouping.PERCENT_STACKED); + + final STBarGrouping.Enum underlying; + + BarGrouping(STBarGrouping.Enum grouping) { + this.underlying = grouping; + } + + private final static HashMap reverse = new HashMap(); + static { + for (BarGrouping value : values()) { + reverse.put(value.underlying, value); + } + } + + static BarGrouping valueOf(STBarGrouping.Enum grouping) { + return reverse.get(grouping); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ChartTypes.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ChartTypes.java new file mode 100644 index 0000000000..9dbec09aaa --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ChartTypes.java @@ -0,0 +1,25 @@ +/* ==================================================================== + 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; + +public enum ChartTypes { + BAR, + LINE, + PIE, + RADAR, + SCATTER; +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/Grouping.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/Grouping.java new file mode 100644 index 0000000000..9549c8755d --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/Grouping.java @@ -0,0 +1,45 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STGrouping; + +public enum Grouping { + STANDARD(STGrouping.STANDARD), + STACKED(STGrouping.STACKED), + PERCENT_STACKED(STGrouping.PERCENT_STACKED); + + final STGrouping.Enum underlying; + + Grouping(STGrouping.Enum grouping) { + this.underlying = grouping; + } + + private final static HashMap reverse = new HashMap(); + static { + for (Grouping value : values()) { + reverse.put(value.underlying, value); + } + } + + static Grouping valueOf(STGrouping.Enum grouping) { + return reverse.get(grouping); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutMode.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutMode.java new file mode 100644 index 0000000000..398d71b3c8 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutMode.java @@ -0,0 +1,44 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutMode;; + +public enum LayoutMode { + EDGE(STLayoutMode.EDGE), + FACTOR(STLayoutMode.FACTOR); + + final STLayoutMode.Enum underlying; + + LayoutMode(STLayoutMode.Enum layoutMode) { + this.underlying = layoutMode; + } + + private final static HashMap reverse = new HashMap(); + static { + for (LayoutMode value : values()) { + reverse.put(value.underlying, value); + } + } + + static LayoutMode valueOf(STLayoutMode.Enum layoutMode) { + return reverse.get(layoutMode); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutTarget.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutTarget.java new file mode 100644 index 0000000000..b4ba3513c0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LayoutTarget.java @@ -0,0 +1,44 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget;; + +public enum LayoutTarget { + INNER(STLayoutTarget.INNER), + OUTER(STLayoutTarget.OUTER); + + final STLayoutTarget.Enum underlying; + + LayoutTarget(STLayoutTarget.Enum layoutTarget) { + this.underlying = layoutTarget; + } + + private final static HashMap reverse = new HashMap(); + static { + for (LayoutTarget value : values()) { + reverse.put(value.underlying, value); + } + } + + static LayoutTarget valueOf(STLayoutTarget.Enum layoutTarget) { + return reverse.get(layoutTarget); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LegendPosition.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LegendPosition.java new file mode 100644 index 0000000000..062a880044 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/LegendPosition.java @@ -0,0 +1,47 @@ +/* ==================================================================== +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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STLegendPos; + +public enum LegendPosition { + BOTTOM(STLegendPos.B), + LEFT(STLegendPos.L), + RIGHT(STLegendPos.R), + TOP(STLegendPos.T), + TOP_RIGHT(STLegendPos.TR); + + final STLegendPos.Enum underlying; + + LegendPosition(STLegendPos.Enum position) { + this.underlying = position; + } + + private final static HashMap reverse = new HashMap(); + static { + for (LegendPosition value : values()) { + reverse.put(value.underlying, value); + } + } + + static LegendPosition valueOf(STLegendPos.Enum position) { + return reverse.get(position); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/MarkerStyle.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/MarkerStyle.java new file mode 100644 index 0000000000..4613fb460e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/MarkerStyle.java @@ -0,0 +1,53 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STMarkerStyle; + +public enum MarkerStyle { + CIRCLE(STMarkerStyle.CIRCLE), + DASH(STMarkerStyle.DASH), + DIAMOND(STMarkerStyle.DIAMOND), + DOT(STMarkerStyle.DOT), + NONE(STMarkerStyle.NONE), + PICTURE(STMarkerStyle.PICTURE), + PLUS(STMarkerStyle.PLUS), + SQUARE(STMarkerStyle.SQUARE), + STAR(STMarkerStyle.STAR), + TRIANGLE(STMarkerStyle.TRIANGLE), + X(STMarkerStyle.X); + + final STMarkerStyle.Enum underlying; + + MarkerStyle(STMarkerStyle.Enum style) { + this.underlying = style; + } + + private final static HashMap reverse = new HashMap(); + static { + for (MarkerStyle value : values()) { + reverse.put(value.underlying, value); + } + } + + static MarkerStyle valueOf(STMarkerStyle.Enum style) { + return reverse.get(style); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/RadarStyle.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/RadarStyle.java new file mode 100644 index 0000000000..8cb7491ad5 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/RadarStyle.java @@ -0,0 +1,45 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STRadarStyle; + +public enum RadarStyle { + FILLED(STRadarStyle.FILLED), + MARKER(STRadarStyle.MARKER), + STANDARD(STRadarStyle.STANDARD); + + final STRadarStyle.Enum underlying; + + RadarStyle(STRadarStyle.Enum style) { + this.underlying = style; + } + + private final static HashMap reverse = new HashMap(); + static { + for (RadarStyle value : values()) { + reverse.put(value.underlying, value); + } + } + + static RadarStyle valueOf(STRadarStyle.Enum style) { + return reverse.get(style); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ScatterStyle.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ScatterStyle.java new file mode 100644 index 0000000000..139bb7d0e4 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/ScatterStyle.java @@ -0,0 +1,48 @@ +/* ==================================================================== + 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 java.util.HashMap; + +import org.openxmlformats.schemas.drawingml.x2006.chart.STScatterStyle; + +public enum ScatterStyle { + LINE(STScatterStyle.LINE), + LINE_MARKER(STScatterStyle.LINE_MARKER), + MARKER(STScatterStyle.MARKER), + NONE(STScatterStyle.NONE), + SMOOTH(STScatterStyle.SMOOTH), + SMOOTH_MARKER(STScatterStyle.SMOOTH_MARKER); + + final STScatterStyle.Enum underlying; + + ScatterStyle(STScatterStyle.Enum style) { + this.underlying = style; + } + + private final static HashMap reverse = new HashMap(); + static { + for (ScatterStyle value : values()) { + reverse.put(value.underlying, value); + } + } + + static ScatterStyle valueOf(STScatterStyle.Enum style) { + return reverse.get(style); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFBarChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFBarChartData.java new file mode 100644 index 0000000000..e89ec321d0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFBarChartData.java @@ -0,0 +1,146 @@ +/* ==================================================================== + 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 java.util.Map; + +import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarSer; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; + +@Beta +public class XDDFBarChartData extends XDDFChartData { + private CTBarChart chart; + + public XDDFBarChartData(CTBarChart chart, Map categories, + Map values) { + this.chart = chart; + for (CTBarSer series : chart.getSerList()) { + this.series.add(new Series(series, series.getCat(), series.getVal())); + } + defineAxes(chart.getAxIdArray(), categories, values); + } + + @Override + public void setVaryColors(boolean varyColors) { + if (chart.isSetVaryColors()) { + chart.getVaryColors().setVal(varyColors); + } else { + chart.addNewVaryColors().setVal(varyColors); + } + } + + public BarDirection getBarDirection() { + return BarDirection.valueOf(chart.getBarDir().getVal()); + } + + public void setBarDirection(BarDirection direction) { + chart.getBarDir().setVal(direction.underlying); + } + + public BarGrouping getBarGrouping() { + if (chart.isSetGrouping()) { + return BarGrouping.valueOf(chart.getGrouping().getVal()); + } else { + return BarGrouping.STANDARD; + } + } + + public void setBarGrouping(BarGrouping grouping) { + if (chart.isSetGrouping()) { + chart.getGrouping().setVal(grouping.underlying); + } else { + chart.addNewGrouping().setVal(grouping.underlying); + } + } + + public int getGapWidth() { + if (chart.isSetGapWidth()) { + return chart.getGapWidth().getVal(); + } else { + return 0; + } + } + + public void setGapWidth(int width) { + if (chart.isSetGapWidth()) { + chart.getGapWidth().setVal(width); + } else { + chart.addNewGapWidth().setVal(width); + } + } + + @Override + public XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values) { + final int index = this.series.size(); + final CTBarSer ctSer = this.chart.addNewSer(); + ctSer.addNewCat(); + ctSer.addNewVal(); + ctSer.addNewIdx().setVal(index); + ctSer.addNewOrder().setVal(index); + final Series added = new Series(ctSer, category, values); + this.series.add(added); + return added; + } + + public class Series extends XDDFChartData.Series { + private CTBarSer series; + + protected Series(CTBarSer series, XDDFDataSource category, + XDDFNumericalDataSource values) { + super(category, values); + this.series = series; + } + + protected Series(CTBarSer series, CTAxDataSource category, CTNumDataSource values) { + super(XDDFDataSourcesFactory.fromDataSource(category), XDDFDataSourcesFactory.fromDataSource(values)); + this.series = series; + } + + @Override + protected CTSerTx getSeriesText() { + return series.getTx(); + } + + @Override + public void setShowLeaderLines(boolean showLeaderLines) { + if (!series.isSetDLbls()) { + series.addNewDLbls(); + } + if (series.getDLbls().isSetShowLeaderLines()) { + series.getDLbls().getShowLeaderLines().setVal(showLeaderLines); + } else { + series.getDLbls().addNewShowLeaderLines().setVal(showLeaderLines); + } + } + + @Override + protected CTAxDataSource getAxDS() { + return series.getCat(); + } + + @Override + protected CTNumDataSource getNumDS() { + return series.getVal(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryAxis.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryAxis.java new file mode 100644 index 0000000000..2744d8b203 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryAxis.java @@ -0,0 +1,151 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTCrosses; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumFmt; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTickMark; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; +import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; + +@Beta +public class XDDFCategoryAxis extends XDDFChartAxis { + + private CTCatAx ctCatAx; + + public XDDFCategoryAxis(CTPlotArea plotArea, AxisPosition position) { + initializeAxis(plotArea, position); + } + + public XDDFCategoryAxis(CTCatAx ctCatAx) { + this.ctCatAx = ctCatAx; + } + + @Override + @Internal + public CTShapeProperties getMajorGridLines() { + if (!ctCatAx.isSetMajorGridlines()) { + ctCatAx.addNewMajorGridlines(); + } + if (!ctCatAx.getMajorGridlines().isSetSpPr()) { + ctCatAx.getMajorGridlines().addNewSpPr(); + } + return ctCatAx.getMajorGridlines().getSpPr(); + } + + @Override + @Internal + public CTShapeProperties getLine() { + return ctCatAx.getSpPr(); + } + + @Override + public void crossAxis(XDDFChartAxis axis) { + ctCatAx.getCrossAx().setVal(axis.getId()); + } + + @Override + protected CTUnsignedInt getCTAxId() { + return ctCatAx.getAxId(); + } + + @Override + protected CTAxPos getCTAxPos() { + return ctCatAx.getAxPos(); + } + + @Override + public boolean hasNumberFormat() { + return ctCatAx.isSetNumFmt(); + } + + @Override + protected CTNumFmt getCTNumFmt() { + if (ctCatAx.isSetNumFmt()) { + return ctCatAx.getNumFmt(); + } + return ctCatAx.addNewNumFmt(); + } + + @Override + protected CTScaling getCTScaling() { + return ctCatAx.getScaling(); + } + + @Override + protected CTCrosses getCTCrosses() { + CTCrosses crosses = ctCatAx.getCrosses(); + if (crosses == null) { + return ctCatAx.addNewCrosses(); + } else { + return crosses; + } + } + + @Override + protected CTBoolean getDelete() { + return ctCatAx.getDelete(); + } + + @Override + protected CTTickMark getMajorCTTickMark() { + return ctCatAx.getMajorTickMark(); + } + + @Override + protected CTTickMark getMinorCTTickMark() { + return ctCatAx.getMinorTickMark(); + } + + public AxisLabelAlignment getLabelAlignment() { + return AxisLabelAlignment.valueOf(ctCatAx.getLblAlgn().getVal()); + } + + public void setLabelAlignment(AxisLabelAlignment labelAlignment) { + ctCatAx.getLblAlgn().setVal(labelAlignment.underlying); + } + + private void initializeAxis(CTPlotArea plotArea, AxisPosition position) { + final long id = getNextAxId(plotArea); + ctCatAx = plotArea.addNewCatAx(); + ctCatAx.addNewAxId().setVal(id); + ctCatAx.addNewAxPos(); + ctCatAx.addNewScaling(); + ctCatAx.addNewCrosses(); + ctCatAx.addNewCrossAx(); + ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO); + ctCatAx.addNewDelete(); + ctCatAx.addNewMajorTickMark(); + ctCatAx.addNewMinorTickMark(); + + setPosition(position); + setOrientation(AxisOrientation.MIN_MAX); + setCrosses(AxisCrosses.AUTO_ZERO); + setVisible(true); + setMajorTickMark(AxisTickMark.CROSS); + setMinorTickMark(AxisTickMark.NONE); + } +} 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 new file mode 100644 index 0000000000..a65db84097 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFCategoryDataSource.java @@ -0,0 +1,26 @@ +/* + * ==================================================================== + * 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 org.apache.poi.util.Beta; + +@Beta +public interface XDDFCategoryDataSource extends XDDFDataSource { +} 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 new file mode 100644 index 0000000000..92f146e2b2 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -0,0 +1,331 @@ +/* + * ==================================================================== + * 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.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; + +import java.io.IOException; +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 org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.xmlbeans.XmlException; +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.CTLineChart; +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.CTSurface; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; +import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; + +@Beta +public abstract class XDDFChart extends POIXMLDocumentPart { + + protected List axes = new ArrayList<>(); + + /** + * Root element of the Chart part + */ + protected final CTChartSpace chartSpace; + + /** + * Chart element in the chart space + */ + protected final CTChart chart; + + /** + * Construct a chart. + */ + protected XDDFChart() { + super(); + + chartSpace = CTChartSpace.Factory.newInstance(); + chart = chartSpace.addNewChart(); + chart.addNewPlotArea(); + } + + /** + * Construct a DrawingML chart from a package part. + * + * @param part the package part holding the chart data, + * the content type must be 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(); + chart = chartSpace.getChart(); + } + + /** + * 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 chart; + + } + + /** + * Return the underlying CTPlotArea bean, within the Chart + * + * @return the underlying CTPlotArea bean + */ + @Internal + protected CTPlotArea getCTPlotArea() { + return chart.getPlotArea(); + } + + /** + * @return true if only visible cells will be present on the chart, + * false otherwise + */ + public boolean isPlotOnlyVisibleCells() { + if (chart.isSetPlotVisOnly()) { + return chart.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 (!chart.isSetPlotVisOnly()) { + chart.setPlotVisOnly(CTBoolean.Factory.newInstance()); + } + chart.getPlotVisOnly().setVal(only); + } + + public void setFloor(int thickness) { + if (!chart.isSetFloor()) { + chart.setFloor(CTSurface.Factory.newInstance()); + } + chart.getFloor().getThickness().setVal(thickness); + } + + public void setBackWall(int thickness) { + if (!chart.isSetBackWall()) { + chart.setBackWall(CTSurface.Factory.newInstance()); + } + chart.getBackWall().getThickness().setVal(thickness); + } + + public void setSideWall(int thickness) { + if (!chart.isSetSideWall()) { + chart.setSideWall(CTSurface.Factory.newInstance()); + } + chart.getSideWall().getThickness().setVal(thickness); + } + + public void setAutoTitleDeleted(boolean deleted) { + if (!chart.isSetAutoTitleDeleted()) { + chart.setAutoTitleDeleted(CTBoolean.Factory.newInstance()); + } + chart.getAutoTitleDeleted().setVal(deleted); + } + + public XDDFChartLegend getOrAddLegend() { + return new XDDFChartLegend(chart); + } + + public void deleteLegend() { + if (chart.isSetLegend()) { + chart.unsetLegend(); + } + } + + public XDDFManualLayout getOrAddManualLayout() { + return new XDDFManualLayout(chart.getPlotArea()); + } + + public void plot(XDDFChartData data) { + for (XDDFChartData.Series series : data.getSeries()) { + series.plot(); + } + } + + public List getChartSeries() { + List series = new LinkedList<>(); + CTPlotArea plotArea = getCTPlotArea(); + Map categories = getCategoryAxes(); + Map values = getValueAxes(); + + for (int i = 0; i < plotArea.sizeOfBarChartArray(); i++) { + CTBarChart barChart = plotArea.getBarChartArray(i); + series.add(new XDDFBarChartData(barChart, categories, values)); + } + + for (int i = 0; i < plotArea.sizeOfLineChartArray(); i++) { + CTLineChart lineChart = plotArea.getLineChartArray(i); + series.add(new XDDFLineChartData(lineChart, categories, values)); + } + + for (int i = 0; i < plotArea.sizeOfPieChartArray(); i++) { + CTPieChart pieChart = plotArea.getPieChartArray(i); + series.add(new XDDFPieChartData(pieChart)); + } + + for (int i = 0; i < plotArea.sizeOfRadarChartArray(); i++) { + CTRadarChart radarChart = plotArea.getRadarChartArray(i); + series.add(new XDDFRadarChartData(radarChart, categories, values)); + } + + for (int i = 0; i < plotArea.sizeOfScatterChartArray(); i++) { + CTScatterChart scatterChart = plotArea.getScatterChartArray(i); + series.add(new XDDFScatterChartData(scatterChart, categories, values)); + } + + // TODO repeat above code for all kind of charts + return series; + } + + private Map getCategoryAxes() { + CTPlotArea plotArea = getCTPlotArea(); + int sizeOfArray = plotArea.sizeOfCatAxArray(); + Map axes = new HashMap(sizeOfArray); + for (int i = 0; i < sizeOfArray; i++) { + CTCatAx category = plotArea.getCatAxArray(i); + axes.put(category.getAxId().getVal(), new XDDFCategoryAxis(category)); + } + return axes; + } + + private Map getValueAxes() { + CTPlotArea plotArea = getCTPlotArea(); + int sizeOfArray = plotArea.sizeOfValAxArray(); + Map axes = new HashMap<>(sizeOfArray); + for (int i = 0; i < sizeOfArray; i++) { + CTValAx values = plotArea.getValAxArray(i); + axes.put(values.getAxId().getVal(), new XDDFValueAxis(values)); + } + return axes; + } + + public XDDFValueAxis createValueAxis(AxisPosition pos) { + XDDFValueAxis valueAxis = new XDDFValueAxis(chart.getPlotArea(), pos); + if (axes.size() == 1) { + XDDFChartAxis axis = axes.get(0); + axis.crossAxis(valueAxis); + valueAxis.crossAxis(axis); + } + axes.add(valueAxis); + return valueAxis; + } + + public XDDFCategoryAxis createCategoryAxis(AxisPosition pos) { + XDDFCategoryAxis categoryAxis = new XDDFCategoryAxis(chart.getPlotArea(), pos); + if (axes.size() == 1) { + XDDFChartAxis axis = axes.get(0); + axis.crossAxis(categoryAxis); + categoryAxis.crossAxis(axis); + } + axes.add(categoryAxis); + return categoryAxis; + } + + public XDDFDateAxis createDateAxis(AxisPosition pos) { + XDDFDateAxis dateAxis = new XDDFDateAxis(chart.getPlotArea(), pos); + if (axes.size() == 1) { + XDDFChartAxis axis = axes.get(0); + axis.crossAxis(dateAxis); + dateAxis.crossAxis(axis); + } + axes.add(dateAxis); + return dateAxis; + } + + public XDDFChartData createData(ChartTypes type, XDDFChartAxis category, XDDFValueAxis values) { + Map categories = Collections.singletonMap(category.getId(), category); + Map mapValues = Collections.singletonMap(values.getId(), values); + final CTPlotArea plotArea = getCTPlotArea(); + switch (type) { + case BAR: + return new XDDFBarChartData(plotArea.addNewBarChart(), categories, mapValues); + case LINE: + return new XDDFLineChartData(plotArea.addNewLineChart(), categories, mapValues); + case PIE: + return new XDDFPieChartData(plotArea.addNewPieChart()); + case RADAR: + return new XDDFRadarChartData(plotArea.addNewRadarChart(), categories, mapValues); + case SCATTER: + return new XDDFScatterChartData(plotArea.addNewScatterChart(), categories, mapValues); + default: + return null; + } + } + + public List getAxes() { + if (axes.isEmpty() && hasAxes()) { + parseAxes(); + } + return axes; + } + + private boolean hasAxes() { + CTPlotArea ctPlotArea = chart.getPlotArea(); + int totalAxisCount = ctPlotArea.sizeOfValAxArray() + ctPlotArea.sizeOfCatAxArray() + ctPlotArea.sizeOfDateAxArray() + ctPlotArea.sizeOfSerAxArray(); + return totalAxisCount > 0; + } + + private void parseAxes() { + // TODO: add other axis types + for (CTCatAx catAx : chart.getPlotArea().getCatAxArray()) { + axes.add(new XDDFCategoryAxis(catAx)); + } + for (CTDateAx dateAx : chart.getPlotArea().getDateAxArray()) { + axes.add(new XDDFDateAxis(dateAx)); + } + for (CTValAx valAx : chart.getPlotArea().getValAxArray()) { + axes.add(new XDDFValueAxis(valAx)); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartAxis.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartAxis.java new file mode 100644 index 0000000000..3279d2b3c2 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartAxis.java @@ -0,0 +1,302 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTCrosses; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLogBase; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumFmt; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTickMark; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; + +/** + * Base class for all axis types. + */ +@Beta +public abstract class XDDFChartAxis { + protected abstract CTUnsignedInt getCTAxId(); + + protected abstract CTAxPos getCTAxPos(); + + protected abstract CTNumFmt getCTNumFmt(); + + protected abstract CTScaling getCTScaling(); + + protected abstract CTCrosses getCTCrosses(); + + protected abstract CTBoolean getDelete(); + + protected abstract CTTickMark getMajorCTTickMark(); + + protected abstract CTTickMark getMinorCTTickMark(); + + @Internal + public abstract CTShapeProperties getMajorGridLines(); + + @Internal + public abstract CTShapeProperties getLine(); + + /** + * @return axis id + */ + public long getId() { + return getCTAxId().getVal(); + } + + /** + * @return axis position + */ + public AxisPosition getPosition() { + return AxisPosition.valueOf(getCTAxPos().getVal()); + } + + /** + * @param position + * new axis position + */ + public void setPosition(AxisPosition position) { + getCTAxPos().setVal(position.underlying); + } + + /** + * Use this to check before retrieving a number format, as calling {@link #getNumberFormat()} may create a default + * one if none exists. + * + * @return true if a number format element is defined, false if not + */ + public abstract boolean hasNumberFormat(); + + /** + * @param format + * axis number format + */ + public void setNumberFormat(String format) { + getCTNumFmt().setFormatCode(format); + getCTNumFmt().setSourceLinked(true); + } + + /** + * @return axis number format + */ + public String getNumberFormat() { + return getCTNumFmt().getFormatCode(); + } + + /** + * @return true if log base is defined, false otherwise + */ + public boolean isSetLogBase() { + return getCTScaling().isSetLogBase(); + } + + private static final double MIN_LOG_BASE = 2.0; + private static final double MAX_LOG_BASE = 1000.0; + + /** + * @param logBase + * a number between 2 and 1000 (inclusive) + * @throws IllegalArgumentException + * if log base not within allowed range + */ + public void setLogBase(double logBase) { + if (logBase < MIN_LOG_BASE || MAX_LOG_BASE < logBase) { + throw new IllegalArgumentException("Axis log base must be between 2 and 1000 (inclusive), got: " + logBase); + } + CTScaling scaling = getCTScaling(); + if (scaling.isSetLogBase()) { + scaling.getLogBase().setVal(logBase); + } else { + scaling.addNewLogBase().setVal(logBase); + } + } + + /** + * @return axis log base or 0.0 if not set + */ + public double getLogBase() { + CTLogBase logBase = getCTScaling().getLogBase(); + if (logBase != null) { + return logBase.getVal(); + } + return 0.0; + } + + /** + * @return true if minimum value is defined, false otherwise + */ + public boolean isSetMinimum() { + return getCTScaling().isSetMin(); + } + + /** + * @param min + * axis minimum + */ + public void setMinimum(double min) { + CTScaling scaling = getCTScaling(); + if (scaling.isSetMin()) { + scaling.getMin().setVal(min); + } else { + scaling.addNewMin().setVal(min); + } + } + + /** + * @return axis minimum or 0.0 if not set + */ + public double getMinimum() { + CTScaling scaling = getCTScaling(); + if (scaling.isSetMin()) { + return scaling.getMin().getVal(); + } else { + return 0.0; + } + } + + /** + * @return true if maximum value is defined, false otherwise + */ + public boolean isSetMaximum() { + return getCTScaling().isSetMax(); + } + + /** + * @param max + * axis maximum + */ + public void setMaximum(double max) { + CTScaling scaling = getCTScaling(); + if (scaling.isSetMax()) { + scaling.getMax().setVal(max); + } else { + scaling.addNewMax().setVal(max); + } + } + + /** + * @return axis maximum or 0.0 if not set + */ + public double getMaximum() { + CTScaling scaling = getCTScaling(); + if (scaling.isSetMax()) { + return scaling.getMax().getVal(); + } else { + return 0.0; + } + } + + /** + * @return axis orientation + */ + public AxisOrientation getOrientation() { + return AxisOrientation.valueOf(getCTScaling().getOrientation().getVal()); + } + + /** + * @param orientation + * axis orientation + */ + public void setOrientation(AxisOrientation orientation) { + CTScaling scaling = getCTScaling(); + if (scaling.isSetOrientation()) { + scaling.getOrientation().setVal(orientation.underlying); + } else { + scaling.addNewOrientation().setVal(orientation.underlying); + } + } + + /** + * @return axis cross type + */ + public AxisCrosses getCrosses() { + return AxisCrosses.valueOf(getCTCrosses().getVal()); + } + + /** + * @param crosses + * axis cross type + */ + public void setCrosses(AxisCrosses crosses) { + getCTCrosses().setVal(crosses.underlying); + } + + /** + * Declare this axis cross another axis. + * + * @param axis + * that this axis should cross + */ + public abstract void crossAxis(XDDFChartAxis axis); + + /** + * @return visibility of the axis. + */ + public boolean isVisible() { + return !getDelete().getVal(); + } + + /** + * @param value + * visibility of the axis. + */ + public void setVisible(boolean value) { + getDelete().setVal(!value); + } + + /** + * @return major tick mark. + */ + public AxisTickMark getMajorTickMark() { + return AxisTickMark.valueOf(getMajorCTTickMark().getVal()); + } + + /** + * @param tickMark + * major tick mark type. + */ + public void setMajorTickMark(AxisTickMark tickMark) { + getMajorCTTickMark().setVal(tickMark.underlying); + } + + /** + * @return minor tick mark. + */ + public AxisTickMark getMinorTickMark() { + return AxisTickMark.valueOf(getMinorCTTickMark().getVal()); + } + + /** + * @param tickMark + * minor tick mark type. + */ + public void setMinorTickMark(AxisTickMark tickMark) { + getMinorCTTickMark().setVal(tickMark.underlying); + } + + protected long getNextAxId(CTPlotArea plotArea) { + long totalAxisCount = plotArea.sizeOfValAxArray() + plotArea.sizeOfCatAxArray() + plotArea.sizeOfDateAxArray() + + plotArea.sizeOfSerAxArray(); + return totalAxisCount; + } +} 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 new file mode 100644 index 0000000000..66f4c9b555 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartData.java @@ -0,0 +1,291 @@ +/* ==================================================================== + 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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrRef; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrVal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; + +/** + * Base of all XDDF Chart Data + */ +@Beta +public abstract class XDDFChartData { + protected List series; + private XDDFCategoryAxis categoryAxis; + private List valueAxes; + + protected XDDFChartData() { + this.series = new ArrayList(); + } + + protected void defineAxes(CTUnsignedInt[] axes, Map categories, + Map values) { + List list = new ArrayList(axes.length); + for (CTUnsignedInt axe : axes) { + Long axisId = axe.getVal(); + XDDFChartAxis category = categories.get(axisId); + if (category == null) { + XDDFValueAxis axis = values.get(axisId); + if (axis != null) { + list.add(axis); + } + } else if (category instanceof XDDFCategoryAxis) { + this.categoryAxis = (XDDFCategoryAxis) category; + } + } + this.valueAxes = Collections.unmodifiableList(list); + } + + public XDDFCategoryAxis getCategoryAxis() { + return categoryAxis; + } + + public List getValueAxes() { + return valueAxes; + } + + public List getSeries() { + return series; + } + + public abstract void setVaryColors(boolean varyColors); + + public abstract XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values); + + public abstract class Series { + protected abstract CTSerTx getSeriesText(); + + public abstract void setShowLeaderLines(boolean showLeaderLines); + + protected XDDFDataSource categoryData; + protected XDDFNumericalDataSource valuesData; + + protected abstract CTAxDataSource getAxDS(); + + protected abstract CTNumDataSource getNumDS(); + + protected Series(XDDFDataSource category, XDDFNumericalDataSource values) { + replaceData(category, values); + } + + public void replaceData(XDDFDataSource category, XDDFNumericalDataSource 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."); + } + this.categoryData = category; + this.valuesData = values; + } + + public void setTitle(String title, CellReference titleRef) { + if (titleRef == null) { + getSeriesText().setV(title); + } else { + CTStrRef ref; + if (getSeriesText().isSetStrRef()) { + ref = getSeriesText().getStrRef(); + } else { + ref = getSeriesText().addNewStrRef(); + } + CTStrData cache; + if (ref.isSetStrCache()) { + cache = ref.getStrCache(); + } else { + cache = ref.addNewStrCache(); + } + cache.getPtArray(0).setV(title); + ref.setF(titleRef.formatAsString()); + } + } + + public XDDFDataSource getCategoryData() { + return categoryData; + } + + public XDDFNumericalDataSource getValuesData() { + return valuesData; + } + + public void plot() { + int numOfPoints = categoryData.getPointCount(); + if (categoryData.isNumeric()) { + CTNumData cache = retrieveNumCache(getAxDS(), categoryData); + fillNumCache(cache, numOfPoints, (XDDFNumericalDataSource) categoryData); + } else { + CTStrData cache = retrieveStrCache(getAxDS(), categoryData); + fillStringCache(cache, numOfPoints, categoryData); + } + CTNumData cache = retrieveNumCache(getNumDS(), valuesData); + fillNumCache(cache, numOfPoints, valuesData); + } + + private CTNumData retrieveNumCache(final CTAxDataSource axDataSource, XDDFDataSource data) { + CTNumData numCache; + if (data.isReference()) { + CTNumRef numRef; + if (axDataSource.isSetNumRef()) { + numRef = axDataSource.getNumRef(); + } else { + numRef = axDataSource.addNewNumRef(); + } + if (numRef.isSetNumCache()) { + numCache = numRef.getNumCache(); + } else { + numCache = numRef.addNewNumCache(); + } + numRef.setF(data.getDataRangeReference()); + if (axDataSource.isSetNumLit()) { + axDataSource.unsetNumLit(); + } + } else { + if (axDataSource.isSetNumLit()) { + numCache = axDataSource.getNumLit(); + } else { + numCache = axDataSource.addNewNumLit(); + } + if (axDataSource.isSetNumRef()) { + axDataSource.unsetNumRef(); + } + } + return numCache; + } + + private CTStrData retrieveStrCache(final CTAxDataSource axDataSource, XDDFDataSource data) { + CTStrData strCache; + if (data.isReference()) { + CTStrRef strRef; + if (axDataSource.isSetStrRef()) { + strRef = axDataSource.getStrRef(); + } else { + strRef = axDataSource.addNewStrRef(); + } + if (strRef.isSetStrCache()) { + strCache = strRef.getStrCache(); + } else { + strCache = strRef.addNewStrCache(); + } + strRef.setF(data.getDataRangeReference()); + if (axDataSource.isSetStrLit()) { + axDataSource.unsetStrLit(); + } + } else { + if (axDataSource.isSetStrLit()) { + strCache = axDataSource.getStrLit(); + } else { + strCache = axDataSource.addNewStrLit(); + } + if (axDataSource.isSetStrRef()) { + axDataSource.unsetStrRef(); + } + } + return strCache; + } + + private CTNumData retrieveNumCache(final CTNumDataSource numDataSource, XDDFDataSource data) { + CTNumData numCache; + if (data.isReference()) { + CTNumRef numRef; + if (numDataSource.isSetNumRef()) { + numRef = numDataSource.getNumRef(); + } else { + numRef = numDataSource.addNewNumRef(); + } + if (numRef.isSetNumCache()) { + numCache = numRef.getNumCache(); + } else { + numCache = numRef.addNewNumCache(); + } + numRef.setF(data.getDataRangeReference()); + if (numDataSource.isSetNumLit()) { + numDataSource.unsetNumLit(); + } + } else { + if (numDataSource.isSetNumLit()) { + numCache = numDataSource.getNumLit(); + } else { + numCache = numDataSource.addNewNumLit(); + } + if (numDataSource.isSetNumRef()) { + numDataSource.unsetNumRef(); + } + } + return numCache; + } + + private void fillStringCache(CTStrData cache, int numOfPoints, XDDFDataSource data) { + cache.setPtArray(null); // unset old values + if (cache.isSetPtCount()) { + cache.getPtCount().setVal(numOfPoints); + } else { + cache.addNewPtCount().setVal(numOfPoints); + } + for (int i = 0; i < numOfPoints; ++i) { + String value = data.getPointAt(i).toString(); + if (value != null) { + CTStrVal ctStrVal = cache.addNewPt(); + ctStrVal.setIdx(i); + ctStrVal.setV(value); + } + } + } + + private void fillNumCache(CTNumData cache, int numOfPoints, XDDFNumericalDataSource data) { + String formatCode = data.getFormatCode(); + if (formatCode == null) { + if (cache.isSetFormatCode()) { + cache.unsetFormatCode(); + } + } else { + cache.setFormatCode(formatCode); + } + cache.setPtArray(null); // unset old values + if (cache.isSetPtCount()) { + cache.getPtCount().setVal(numOfPoints); + } else { + cache.addNewPtCount().setVal(numOfPoints); + } + for (int i = 0; i < numOfPoints; ++i) { + Object value = data.getPointAt(i); + if (value != null) { + CTNumVal ctNumVal = cache.addNewPt(); + ctNumVal.setIdx(i); + ctNumVal.setV(value.toString()); + } + } + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartExtensionList.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartExtensionList.java new file mode 100644 index 0000000000..9e593edfbb --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartExtensionList.java @@ -0,0 +1,41 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTExtensionList; + +@Beta +public class XDDFChartExtensionList { + private CTExtensionList list; + + public XDDFChartExtensionList() { + this(CTExtensionList.Factory.newInstance()); + } + + @Internal + protected XDDFChartExtensionList(CTExtensionList list) { + this.list = list; + } + + @Internal + public CTExtensionList getXmlObject() { + return list; + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartLegend.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartLegend.java new file mode 100644 index 0000000000..75db5ebbd4 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChartLegend.java @@ -0,0 +1,184 @@ +/* ==================================================================== + 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 java.util.List; +import java.util.stream.Collectors; + +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.poi.xddf.usermodel.text.XDDFTextBody; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLegend; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; + +/** + * Represents a DrawingML chart legend + */ +@Beta +public final class XDDFChartLegend { + + /** + * Underlying CTLegend bean + */ + private CTLegend legend; + + /** + * Create a new DrawingML chart legend + */ + public XDDFChartLegend(CTChart ctChart) { + this.legend = (ctChart.isSetLegend()) ? ctChart.getLegend() : ctChart.addNewLegend(); + + setDefaults(); + } + + /** + * Set sensible default styling. + */ + private void setDefaults() { + if (!legend.isSetOverlay()) { + legend.addNewOverlay(); + } + legend.getOverlay().setVal(false); + } + + /** + * Return the underlying CTLegend bean. + * + * @return the underlying CTLegend bean + */ + @Internal + protected CTLegend getXmlObject() { + return legend; + } + + @Internal // will later replace with XDDFShapeProperties + public CTShapeProperties getShapeProperties() { + if (legend.isSetSpPr()) { + return legend.getSpPr(); + } else { + return null; + } + } + + @Internal // will later replace with XDDFShapeProperties + public void setShapeProperties(CTShapeProperties properties) { + if (properties == null) { + legend.unsetSpPr(); + } else { + legend.setSpPr(properties); + } + } + + public XDDFTextBody getTextBody() { + if (legend.isSetTxPr()) { + return new XDDFTextBody(legend.getTxPr()); + } else { + return null; + } + } + + public void setTextBody(XDDFTextBody body) { + if (body == null) { + legend.unsetTxPr(); + } else { + legend.setTxPr(body.getXmlObject()); + } + } + + public XDDFLegendEntry addEntry() { + return new XDDFLegendEntry(legend.addNewLegendEntry()); + } + + public XDDFLegendEntry getEntry(int index) { + return new XDDFLegendEntry(legend.getLegendEntryArray(index)); + } + + public List getEntries() { + return legend + .getLegendEntryList() + .stream() + .map(entry -> new XDDFLegendEntry(entry)) + .collect(Collectors.toList()); + } + + public void setExtensionList(XDDFChartExtensionList list) { + if (list == null) { + legend.unsetExtLst(); + } else { + legend.setExtLst(list.getXmlObject()); + } + } + + public XDDFChartExtensionList getExtensionList() { + if (legend.isSetExtLst()) { + return new XDDFChartExtensionList(legend.getExtLst()); + } else { + return null; + } + } + + public void setLayout(XDDFLayout layout) { + if (layout == null) { + legend.unsetLayout(); + } else { + legend.setLayout(layout.getXmlObject()); + } + } + + public XDDFLayout getLayout() { + if (legend.isSetLayout()) { + return new XDDFLayout(legend.getLayout()); + } else { + return null; + } + } + + public void setPosition(LegendPosition position) { + if (!legend.isSetLegendPos()) { + legend.addNewLegendPos(); + } + legend.getLegendPos().setVal(position.underlying); + } + + /* + * According to ECMA-376 default position is RIGHT. + */ + public LegendPosition getPosition() { + if (legend.isSetLegendPos()) { + return LegendPosition.valueOf(legend.getLegendPos().getVal()); + } else { + return LegendPosition.RIGHT; + } + } + + public XDDFManualLayout getOrAddManualLayout() { + if (!legend.isSetLayout()) { + legend.addNewLayout(); + } + return new XDDFManualLayout(legend.getLayout()); + } + + public boolean isOverlay() { + return legend.getOverlay().getVal(); + } + + public void setOverlay(boolean value) { + legend.getOverlay().setVal(value); + } +} 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 new file mode 100644 index 0000000000..f6c19a1d1d --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java @@ -0,0 +1,35 @@ +/* + * ==================================================================== + * 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 org.apache.poi.util.Beta; + +@Beta +public interface XDDFDataSource { + int getPointCount(); + + T getPointAt(int index); + + boolean isReference(); + + boolean isNumeric(); + + String getDataRangeReference(); +} 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 new file mode 100644 index 0000000000..a740d92f4f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -0,0 +1,299 @@ +/* + * ==================================================================== + * 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 org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.CellValue; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.util.Beta; +import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData; + +/** + * Class {@code XDDFDataSourcesFactory} is a factory for {@link XDDFDataSource} instances. + */ +@Beta +public class XDDFDataSourcesFactory { + + private 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(); + } + }; + } + + public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { + return new XDDFNumericalDataSource() { + private CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return true; + } + + @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 valuesDS.getNumRef().getF(); + } + }; + } + + public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { + return new NumericalArrayDataSource(elements, dataRange); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { + return new StringArrayDataSource(elements, dataRange); + } + + public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, + CellRangeAddress cellRangeAddress) { + return new NumericalCellRangeDataSource(sheet, cellRangeAddress); + } + + public static XDDFCategoryDataSource fromStringCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + return new StringCellRangeDataSource(sheet, cellRangeAddress); + } + + private abstract static class AbstractArrayDataSource implements XDDFDataSource { + private final T[] elements; + private final String dataRange; + + public AbstractArrayDataSource(T[] elements, String dataRange) { + this.elements = elements.clone(); + this.dataRange = dataRange; + } + + @Override + public int getPointCount() { + return elements.length; + } + + @Override + public T getPointAt(int index) { + return elements[index]; + } + + @Override + public boolean isReference() { + return dataRange != null; + } + + @Override + public boolean isNumeric() { + Class arrayComponentType = elements.getClass().getComponentType(); + return (Number.class.isAssignableFrom(arrayComponentType)); + } + + @Override + public String getDataRangeReference() { + if (dataRange == null) { + throw new UnsupportedOperationException("Literal data source can not be expressed by reference."); + } else { + return dataRange; + } + } + } + + private static class NumericalArrayDataSource extends AbstractArrayDataSource + implements XDDFNumericalDataSource { + private String formatCode; + + public NumericalArrayDataSource(T[] elements, String dataRange) { + super(elements, dataRange); + } + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + } + + private static class StringArrayDataSource extends AbstractArrayDataSource + implements XDDFCategoryDataSource { + public StringArrayDataSource(String[] elements, String dataRange) { + super(elements, dataRange); + } + } + + private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { + private final XSSFSheet sheet; + private final CellRangeAddress cellRangeAddress; + private final int numOfCells; + private XSSFFormulaEvaluator evaluator; + + protected AbstractCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + this.sheet = sheet; + // Make copy since CellRangeAddress is mutable. + this.cellRangeAddress = cellRangeAddress.copy(); + this.numOfCells = this.cellRangeAddress.getNumberOfCells(); + this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); + } + + @Override + public int getPointCount() { + return numOfCells; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public String getDataRangeReference() { + return cellRangeAddress.formatAsString(sheet.getSheetName(), true); + } + + protected CellValue getCellValueAt(int index) { + if (index < 0 || index >= numOfCells) { + throw new IndexOutOfBoundsException( + "Index must be between 0 and " + (numOfCells - 1) + " (inclusive), given: " + index); + } + int firstRow = cellRangeAddress.getFirstRow(); + int firstCol = cellRangeAddress.getFirstColumn(); + int lastCol = cellRangeAddress.getLastColumn(); + int width = lastCol - firstCol + 1; + int rowIndex = firstRow + index / width; + int cellIndex = firstCol + index % width; + XSSFRow row = sheet.getRow(rowIndex); + return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex)); + } + } + + private static class NumericalCellRangeDataSource extends AbstractCellRangeDataSource + implements XDDFNumericalDataSource { + protected NumericalCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + private String formatCode; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public Double getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellTypeEnum() == CellType.NUMERIC) { + return Double.valueOf(cellValue.getNumberValue()); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return true; + } + } + + private static class StringCellRangeDataSource extends AbstractCellRangeDataSource + implements XDDFCategoryDataSource { + protected StringCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + @Override + public String getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellTypeEnum() == CellType.STRING) { + return cellValue.getStringValue(); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return false; + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDateAxis.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDateAxis.java new file mode 100644 index 0000000000..bc72a2162e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDateAxis.java @@ -0,0 +1,147 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTCrosses; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTDateAx; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumFmt; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTickMark; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; +import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; + +/** + * Date axis type. Currently only implements the same values as {@link XDDFCategoryAxis}, since the two are nearly + * identical. + */ +@Beta +public class XDDFDateAxis extends XDDFChartAxis { + + private CTDateAx ctDateAx; + + public XDDFDateAxis(CTPlotArea plotArea, AxisPosition position) { + initializeAxis(plotArea, position); + } + + public XDDFDateAxis(CTDateAx ctDateAx) { + this.ctDateAx = ctDateAx; + } + + @Override + @Internal + public CTShapeProperties getMajorGridLines() { + if (!ctDateAx.isSetMajorGridlines()) { + ctDateAx.addNewMajorGridlines(); + } + if (!ctDateAx.getMajorGridlines().isSetSpPr()) { + ctDateAx.getMajorGridlines().addNewSpPr(); + } + return ctDateAx.getMajorGridlines().getSpPr(); + } + + @Override + @Internal + public CTShapeProperties getLine() { + return ctDateAx.getSpPr(); + } + + @Override + public void crossAxis(XDDFChartAxis axis) { + ctDateAx.getCrossAx().setVal(axis.getId()); + } + + @Override + protected CTUnsignedInt getCTAxId() { + return ctDateAx.getAxId(); + } + + @Override + protected CTAxPos getCTAxPos() { + return ctDateAx.getAxPos(); + } + + @Override + public boolean hasNumberFormat() { + return ctDateAx.isSetNumFmt(); + } + + @Override + protected CTNumFmt getCTNumFmt() { + if (ctDateAx.isSetNumFmt()) { + return ctDateAx.getNumFmt(); + } + return ctDateAx.addNewNumFmt(); + } + + @Override + protected CTScaling getCTScaling() { + return ctDateAx.getScaling(); + } + + @Override + protected CTCrosses getCTCrosses() { + CTCrosses crosses = ctDateAx.getCrosses(); + if (crosses == null) { + return ctDateAx.addNewCrosses(); + } else { + return crosses; + } + } + + @Override + protected CTBoolean getDelete() { + return ctDateAx.getDelete(); + } + + @Override + protected CTTickMark getMajorCTTickMark() { + return ctDateAx.getMajorTickMark(); + } + + @Override + protected CTTickMark getMinorCTTickMark() { + return ctDateAx.getMinorTickMark(); + } + + private void initializeAxis(CTPlotArea plotArea, AxisPosition position) { + final long id = getNextAxId(plotArea); + ctDateAx = plotArea.addNewDateAx(); + ctDateAx.addNewAxId().setVal(id); + ctDateAx.addNewAxPos(); + ctDateAx.addNewScaling(); + ctDateAx.addNewCrosses(); + ctDateAx.addNewCrossAx(); + ctDateAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO); + ctDateAx.addNewDelete(); + ctDateAx.addNewMajorTickMark(); + ctDateAx.addNewMinorTickMark(); + + setPosition(position); + setOrientation(AxisOrientation.MIN_MAX); + setCrosses(AxisCrosses.AUTO_ZERO); + setVisible(true); + setMajorTickMark(AxisTickMark.CROSS); + setMinorTickMark(AxisTickMark.NONE); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLayout.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLayout.java new file mode 100644 index 0000000000..b3bacdd649 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLayout.java @@ -0,0 +1,73 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLayout; + +@Beta +public class XDDFLayout { + private CTLayout layout; + + public XDDFLayout() { + this(CTLayout.Factory.newInstance()); + } + + @Internal + protected XDDFLayout(CTLayout layout) { + this.layout = layout; + } + + @Internal + protected CTLayout getXmlObject() { + return layout; + } + + public void setExtensionList(XDDFChartExtensionList list) { + if (list == null) { + layout.unsetExtLst(); + } else { + layout.setExtLst(list.getXmlObject()); + } + } + + public XDDFChartExtensionList getExtensionList() { + if (layout.isSetExtLst()) { + return new XDDFChartExtensionList(layout.getExtLst()); + } else { + return null; + } + } + + public void setManualLayout(XDDFManualLayout manual) { + if (manual == null) { + layout.unsetManualLayout(); + } else { + layout.setManualLayout(manual.getXmlObject()); + } + } + + public XDDFManualLayout getManualLayout() { + if (layout.isSetManualLayout()) { + return new XDDFManualLayout(layout); + } else { + return null; + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLegendEntry.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLegendEntry.java new file mode 100644 index 0000000000..da8fa22c85 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLegendEntry.java @@ -0,0 +1,98 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.apache.poi.xddf.usermodel.text.XDDFTextBody; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLegendEntry; + +@Beta +public class XDDFLegendEntry { + private CTLegendEntry entry; + + @Internal + protected XDDFLegendEntry(CTLegendEntry entry) { + this.entry = entry; + } + + @Internal + protected CTLegendEntry getXmlObject() { + return entry; + } + + public XDDFTextBody getTextBody() { + if (entry.isSetTxPr()) { + return new XDDFTextBody(entry.getTxPr()); + } else { + return null; + } + } + + public void setTextBody(XDDFTextBody body) { + if (body == null) { + entry.unsetTxPr(); + } else { + entry.setTxPr(body.getXmlObject()); + } + } + + public boolean getDelete() { + if (entry.isSetDelete()) { + return entry.getDelete().getVal(); + } else { + return false; + } + } + + public void setDelete(Boolean delete) { + if (delete == null) { + entry.unsetDelete(); + } else { + if (entry.isSetDelete()) { + entry.getDelete().setVal(delete); + } else { + entry.addNewDelete().setVal(delete); + } + } + } + + public long getIndex() { + return entry.getIdx().getVal(); + } + + public void setIndex(long index) { + entry.getIdx().setVal(index); + } + + public void setExtensionList(XDDFChartExtensionList list) { + if (list == null) { + entry.unsetExtLst(); + } else { + entry.setExtLst(list.getXmlObject()); + } + } + + public XDDFChartExtensionList getExtensionList() { + if (entry.isSetExtLst()) { + return new XDDFChartExtensionList(entry.getExtLst()); + } else { + return null; + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLineChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLineChartData.java new file mode 100644 index 0000000000..ff4c648b61 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFLineChartData.java @@ -0,0 +1,141 @@ +/* ==================================================================== + 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 java.util.Map; + +import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTMarker; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; + +@Beta +public class XDDFLineChartData extends XDDFChartData { + private CTLineChart chart; + + public XDDFLineChartData(CTLineChart chart, Map categories, + Map values) { + this.chart = chart; + for (CTLineSer series : chart.getSerList()) { + this.series.add(new Series(series, series.getCat(), series.getVal())); + } + defineAxes(chart.getAxIdArray(), categories, values); + } + + @Override + public void setVaryColors(boolean varyColors) { + if (chart.isSetVaryColors()) { + chart.getVaryColors().setVal(varyColors); + } else { + chart.addNewVaryColors().setVal(varyColors); + } + } + + public Grouping getGrouping() { + return Grouping.valueOf(chart.getGrouping().getVal()); + } + + public void setGrouping(Grouping grouping) { + chart.getGrouping().setVal(grouping.underlying); + } + + @Override + public XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values) { + final int index = this.series.size(); + final CTLineSer ctSer = this.chart.addNewSer(); + ctSer.addNewCat(); + ctSer.addNewVal(); + ctSer.addNewIdx().setVal(index); + ctSer.addNewOrder().setVal(index); + final Series added = new Series(ctSer, category, values); + this.series.add(added); + return added; + } + + public class Series extends XDDFChartData.Series { + private CTLineSer series; + + protected Series(CTLineSer series, XDDFDataSource category, + XDDFNumericalDataSource values) { + super(category, values); + this.series = series; + } + + protected Series(CTLineSer series, CTAxDataSource category, CTNumDataSource values) { + super(XDDFDataSourcesFactory.fromDataSource(category), XDDFDataSourcesFactory.fromDataSource(values)); + this.series = series; + } + + @Override + protected CTSerTx getSeriesText() { + return series.getTx(); + } + + @Override + public void setShowLeaderLines(boolean showLeaderLines) { + if (!series.isSetDLbls()) { + series.addNewDLbls(); + } + if (series.getDLbls().isSetShowLeaderLines()) { + series.getDLbls().getShowLeaderLines().setVal(showLeaderLines); + } else { + series.getDLbls().addNewShowLeaderLines().setVal(showLeaderLines); + } + } + + public void setMarkerSize(short size) { + CTMarker marker = getMarker(); + if (marker.isSetSize()) { + marker.getSize().setVal(size); + } else { + marker.addNewSize().setVal(size); + } + } + + public void setMarkerStyle(MarkerStyle style) { + CTMarker marker = getMarker(); + if (marker.isSetSymbol()) { + marker.getSymbol().setVal(style.underlying); + } else { + marker.addNewSymbol().setVal(style.underlying); + } + } + + private CTMarker getMarker() { + if (series.isSetMarker()) { + return series.getMarker(); + } else { + return series.addNewMarker(); + } + } + + @Override + protected CTAxDataSource getAxDS() { + return series.getCat(); + } + + @Override + protected CTNumDataSource getNumDS() { + return series.getVal(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFManualLayout.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFManualLayout.java new file mode 100644 index 0000000000..766fb95507 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFManualLayout.java @@ -0,0 +1,221 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTLayout; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTManualLayout; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; + +/** + * Represents a DrawingML manual layout. + */ +@Beta +public final class XDDFManualLayout { + + /** + * Underlaying CTManualLayout bean. + */ + private CTManualLayout layout; + + private static final LayoutMode defaultLayoutMode = LayoutMode.EDGE; + private static final LayoutTarget defaultLayoutTarget = LayoutTarget.INNER; + + /** + * Create a new DrawingML manual layout. + * + * @param ctLayout + * a DrawingML layout that should be used as base. + */ + public XDDFManualLayout(CTLayout ctLayout) { + initializeLayout(ctLayout); + } + + /** + * Create a new DrawingML manual layout for chart. + * + * @param ctPlotArea + * a chart's plot area to create layout for. + */ + public XDDFManualLayout(CTPlotArea ctPlotArea) { + CTLayout ctLayout = ctPlotArea.isSetLayout() ? ctPlotArea.getLayout() : ctPlotArea.addNewLayout(); + + initializeLayout(ctLayout); + } + + /** + * Return the underlying CTManualLayout bean. + * + * @return the underlying CTManualLayout bean. + */ + @Internal + protected CTManualLayout getXmlObject() { + return layout; + } + + public void setExtensionList(XDDFChartExtensionList list) { + if (list == null) { + layout.unsetExtLst(); + } else { + layout.setExtLst(list.getXmlObject()); + } + } + + public XDDFChartExtensionList getExtensionList() { + if (layout.isSetExtLst()) { + return new XDDFChartExtensionList(layout.getExtLst()); + } else { + return null; + } + } + + public void setWidthRatio(double ratio) { + if (!layout.isSetW()) { + layout.addNewW(); + } + layout.getW().setVal(ratio); + } + + public double getWidthRatio() { + if (!layout.isSetW()) { + return 0.0; + } + return layout.getW().getVal(); + } + + public void setHeightRatio(double ratio) { + if (!layout.isSetH()) { + layout.addNewH(); + } + layout.getH().setVal(ratio); + } + + public double getHeightRatio() { + if (!layout.isSetH()) { + return 0.0; + } + return layout.getH().getVal(); + } + + public LayoutTarget getTarget() { + if (!layout.isSetLayoutTarget()) { + return defaultLayoutTarget; + } + return LayoutTarget.valueOf(layout.getLayoutTarget().getVal()); + } + + public void setTarget(LayoutTarget target) { + if (!layout.isSetLayoutTarget()) { + layout.addNewLayoutTarget(); + } + layout.getLayoutTarget().setVal(target.underlying); + } + + public LayoutMode getXMode() { + if (!layout.isSetXMode()) { + return defaultLayoutMode; + } + return LayoutMode.valueOf(layout.getXMode().getVal()); + } + + public void setXMode(LayoutMode mode) { + if (!layout.isSetXMode()) { + layout.addNewXMode(); + } + layout.getXMode().setVal(mode.underlying); + } + + public LayoutMode getYMode() { + if (!layout.isSetYMode()) { + return defaultLayoutMode; + } + return LayoutMode.valueOf(layout.getYMode().getVal()); + } + + public void setYMode(LayoutMode mode) { + if (!layout.isSetYMode()) { + layout.addNewYMode(); + } + layout.getYMode().setVal(mode.underlying); + } + + public double getX() { + if (!layout.isSetX()) { + return 0.0; + } + return layout.getX().getVal(); + } + + public void setX(double x) { + if (!layout.isSetX()) { + layout.addNewX(); + } + layout.getX().setVal(x); + } + + public double getY() { + if (!layout.isSetY()) { + return 0.0; + } + return layout.getY().getVal(); + } + + public void setY(double y) { + if (!layout.isSetY()) { + layout.addNewY(); + } + layout.getY().setVal(y); + } + + public LayoutMode getWidthMode() { + if (!layout.isSetWMode()) { + return defaultLayoutMode; + } + return LayoutMode.valueOf(layout.getWMode().getVal()); + } + + public void setWidthMode(LayoutMode mode) { + if (!layout.isSetWMode()) { + layout.addNewWMode(); + } + layout.getWMode().setVal(mode.underlying); + } + + public LayoutMode getHeightMode() { + if (!layout.isSetHMode()) { + return defaultLayoutMode; + } + return LayoutMode.valueOf(layout.getHMode().getVal()); + } + + public void setHeightMode(LayoutMode mode) { + if (!layout.isSetHMode()) { + layout.addNewHMode(); + } + layout.getHMode().setVal(mode.underlying); + } + + private void initializeLayout(CTLayout ctLayout) { + if (ctLayout.isSetManualLayout()) { + this.layout = ctLayout.getManualLayout(); + } else { + this.layout = ctLayout.addNewManualLayout(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFNumericalDataSource.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFNumericalDataSource.java new file mode 100644 index 0000000000..bb6d0825a8 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFNumericalDataSource.java @@ -0,0 +1,29 @@ +/* + * ==================================================================== + * 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 org.apache.poi.util.Beta; + +@Beta +public interface XDDFNumericalDataSource extends XDDFDataSource { + String getFormatCode(); + + void setFormatCode(String formatCode); +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFPieChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFPieChartData.java new file mode 100644 index 0000000000..5c773c272d --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFPieChartData.java @@ -0,0 +1,118 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; + +@Beta +public class XDDFPieChartData extends XDDFChartData { + private CTPieChart chart; + + public XDDFPieChartData(CTPieChart chart) { + this.chart = chart; + for (CTPieSer series : chart.getSerList()) { + this.series.add(new Series(series, series.getCat(), series.getVal())); + } + } + + @Override + public void setVaryColors(boolean varyColors) { + if (chart.isSetVaryColors()) { + chart.getVaryColors().setVal(varyColors); + } else { + chart.addNewVaryColors().setVal(varyColors); + } + } + + @Override + public XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values) { + final int index = this.series.size(); + final CTPieSer ctSer = this.chart.addNewSer(); + ctSer.addNewCat(); + ctSer.addNewVal(); + ctSer.addNewIdx().setVal(index); + ctSer.addNewOrder().setVal(index); + final Series added = new Series(ctSer, category, values); + this.series.add(added); + return added; + } + + public class Series extends XDDFChartData.Series { + private CTPieSer series; + + protected Series(CTPieSer series, XDDFDataSource category, + XDDFNumericalDataSource values) { + super(category, values); + this.series = series; + } + + protected Series(CTPieSer series, CTAxDataSource category, CTNumDataSource values) { + super(XDDFDataSourcesFactory.fromDataSource(category), XDDFDataSourcesFactory.fromDataSource(values)); + this.series = series; + } + + @Override + protected CTSerTx getSeriesText() { + return series.getTx(); + } + + @Override + public void setShowLeaderLines(boolean showLeaderLines) { + if (!series.isSetDLbls()) { + series.addNewDLbls(); + } + if (series.getDLbls().isSetShowLeaderLines()) { + series.getDLbls().getShowLeaderLines().setVal(showLeaderLines); + } else { + series.getDLbls().addNewShowLeaderLines().setVal(showLeaderLines); + } + } + + public long getExplosion() { + if (series.isSetExplosion()) { + return series.getExplosion().getVal(); + } else { + return 0; + } + } + + public void setExplosion(long explosion) { + if (series.isSetExplosion()) { + series.getExplosion().setVal(explosion); + } else { + series.addNewExplosion().setVal(explosion); + } + } + + @Override + protected CTAxDataSource getAxDS() { + return series.getCat(); + } + + @Override + protected CTNumDataSource getNumDS() { + return series.getVal(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFRadarChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFRadarChartData.java new file mode 100644 index 0000000000..15891ce58b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFRadarChartData.java @@ -0,0 +1,119 @@ +/* ==================================================================== + 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 java.util.Map; + +import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTRadarChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTRadarSer; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTRadarStyle; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; + +@Beta +public class XDDFRadarChartData extends XDDFChartData { + private CTRadarChart chart; + + public XDDFRadarChartData(CTRadarChart chart, Map categories, + Map values) { + this.chart = chart; + for (CTRadarSer series : chart.getSerList()) { + this.series.add(new Series(series, series.getCat(), series.getVal())); + } + defineAxes(chart.getAxIdArray(), categories, values); + } + + @Override + public void setVaryColors(boolean varyColors) { + if (chart.isSetVaryColors()) { + chart.getVaryColors().setVal(varyColors); + } else { + chart.addNewVaryColors().setVal(varyColors); + } + } + + public RadarStyle getStyle() { + return RadarStyle.valueOf(chart.getRadarStyle().getVal()); + } + + public void setStyle(RadarStyle style) { + CTRadarStyle radarStyle = chart.getRadarStyle(); + if (radarStyle == null) { + radarStyle = chart.addNewRadarStyle(); + } + radarStyle.setVal(style.underlying); + } + + @Override + public XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values) { + final int index = this.series.size(); + final CTRadarSer ctSer = this.chart.addNewSer(); + ctSer.addNewCat(); + ctSer.addNewVal(); + ctSer.addNewIdx().setVal(index); + ctSer.addNewOrder().setVal(index); + final Series added = new Series(ctSer, category, values); + this.series.add(added); + return added; + } + + public class Series extends XDDFChartData.Series { + private CTRadarSer series; + + protected Series(CTRadarSer series, XDDFDataSource category, + XDDFNumericalDataSource values) { + super(category, values); + this.series = series; + } + + protected Series(CTRadarSer series, CTAxDataSource category, CTNumDataSource values) { + super(XDDFDataSourcesFactory.fromDataSource(category), XDDFDataSourcesFactory.fromDataSource(values)); + this.series = series; + } + + @Override + protected CTSerTx getSeriesText() { + return series.getTx(); + } + + @Override + public void setShowLeaderLines(boolean showLeaderLines) { + if (!series.isSetDLbls()) { + series.addNewDLbls(); + } + if (series.getDLbls().isSetShowLeaderLines()) { + series.getDLbls().getShowLeaderLines().setVal(showLeaderLines); + } else { + series.getDLbls().addNewShowLeaderLines().setVal(showLeaderLines); + } + } + + @Override + protected CTAxDataSource getAxDS() { + return series.getCat(); + } + + @Override + protected CTNumDataSource getNumDS() { + return series.getVal(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFScatterChartData.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFScatterChartData.java new file mode 100644 index 0000000000..f025273a24 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFScatterChartData.java @@ -0,0 +1,123 @@ +/* ==================================================================== + 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 java.util.Map; + +import org.apache.poi.util.Beta; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterChart; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterSer; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterStyle; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; + +@Beta +public class XDDFScatterChartData extends XDDFChartData { + private CTScatterChart chart; + + public XDDFScatterChartData(CTScatterChart chart, Map categories, + Map values) { + this.chart = chart; + for (CTScatterSer series : chart.getSerList()) { + this.series.add(new Series(series, series.getXVal(), series.getYVal())); + } + defineAxes(chart.getAxIdArray(), categories, values); + } + + @Override + public void setVaryColors(boolean varyColors) { + if (chart.isSetVaryColors()) { + chart.getVaryColors().setVal(varyColors); + } else { + chart.addNewVaryColors().setVal(varyColors); + } + } + + public ScatterStyle getStyle() { + CTScatterStyle scatterStyle = chart.getScatterStyle(); + if (scatterStyle == null) { + scatterStyle = chart.addNewScatterStyle(); + scatterStyle.setVal(ScatterStyle.LINE_MARKER.underlying); + } + return ScatterStyle.valueOf(scatterStyle.getVal()); + } + + public void setStyle(ScatterStyle style) { + CTScatterStyle scatterStyle = chart.getScatterStyle(); + if (scatterStyle == null) { + scatterStyle = chart.addNewScatterStyle(); + } + scatterStyle.setVal(style.underlying); + } + + @Override + public XDDFChartData.Series addSeries(XDDFDataSource category, + XDDFNumericalDataSource values) { + final int index = this.series.size(); + final CTScatterSer ctSer = this.chart.addNewSer(); + ctSer.addNewXVal(); + ctSer.addNewYVal(); + ctSer.addNewIdx().setVal(index); + ctSer.addNewOrder().setVal(index); + final Series added = new Series(ctSer, category, values); + this.series.add(added); + return added; + } + + public class Series extends XDDFChartData.Series { + private CTScatterSer series; + + protected Series(CTScatterSer series, XDDFDataSource category, XDDFNumericalDataSource values) { + super(category, values); + this.series = series; + } + + protected Series(CTScatterSer series, CTAxDataSource category, CTNumDataSource values) { + super(XDDFDataSourcesFactory.fromDataSource(category), XDDFDataSourcesFactory.fromDataSource(values)); + this.series = series; + } + + @Override + protected CTSerTx getSeriesText() { + return series.getTx(); + } + + @Override + public void setShowLeaderLines(boolean showLeaderLines) { + if (!series.isSetDLbls()) { + series.addNewDLbls(); + } + if (series.getDLbls().isSetShowLeaderLines()) { + series.getDLbls().getShowLeaderLines().setVal(showLeaderLines); + } else { + series.getDLbls().addNewShowLeaderLines().setVal(showLeaderLines); + } + } + + @Override + protected CTAxDataSource getAxDS() { + return series.getXVal(); + } + + @Override + protected CTNumDataSource getNumDS() { + return series.getYVal(); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFValueAxis.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFValueAxis.java new file mode 100644 index 0000000000..08fe0dea15 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFValueAxis.java @@ -0,0 +1,153 @@ +/* ==================================================================== + 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 org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTCrosses; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumFmt; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTickMark; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; +import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; + +@Beta +public class XDDFValueAxis extends XDDFChartAxis { + + private CTValAx ctValAx; + + public XDDFValueAxis(CTPlotArea plotArea, AxisPosition position) { + initializeAxis(plotArea, position); + } + + public XDDFValueAxis(CTValAx ctValAx) { + this.ctValAx = ctValAx; + } + + @Override + @Internal + public CTShapeProperties getMajorGridLines() { + if (!ctValAx.isSetMajorGridlines()) { + ctValAx.addNewMajorGridlines(); + } + if (!ctValAx.getMajorGridlines().isSetSpPr()) { + ctValAx.getMajorGridlines().addNewSpPr(); + } + return ctValAx.getMajorGridlines().getSpPr(); + } + + @Override + @Internal + public CTShapeProperties getLine() { + return ctValAx.getSpPr(); + } + + @Override + public void crossAxis(XDDFChartAxis axis) { + ctValAx.getCrossAx().setVal(axis.getId()); + } + + @Override + protected CTUnsignedInt getCTAxId() { + return ctValAx.getAxId(); + } + + @Override + protected CTAxPos getCTAxPos() { + return ctValAx.getAxPos(); + } + + @Override + public boolean hasNumberFormat() { + return ctValAx.isSetNumFmt(); + } + + @Override + protected CTNumFmt getCTNumFmt() { + if (ctValAx.isSetNumFmt()) { + return ctValAx.getNumFmt(); + } + return ctValAx.addNewNumFmt(); + } + + @Override + protected CTScaling getCTScaling() { + return ctValAx.getScaling(); + } + + @Override + protected CTCrosses getCTCrosses() { + CTCrosses crosses = ctValAx.getCrosses(); + if (crosses == null) { + return ctValAx.addNewCrosses(); + } else { + return crosses; + } + } + + @Override + protected CTBoolean getDelete() { + return ctValAx.getDelete(); + } + + @Override + protected CTTickMark getMajorCTTickMark() { + return ctValAx.getMajorTickMark(); + } + + @Override + protected CTTickMark getMinorCTTickMark() { + return ctValAx.getMinorTickMark(); + } + + public AxisCrossBetween getCrossBetween() { + return AxisCrossBetween.valueOf(ctValAx.getCrossBetween().getVal()); + } + + public void setCrossBetween(AxisCrossBetween crossBetween) { + ctValAx.getCrossBetween().setVal(crossBetween.underlying); + } + + private void initializeAxis(CTPlotArea plotArea, AxisPosition position) { + final long id = getNextAxId(plotArea); + ctValAx = plotArea.addNewValAx(); + ctValAx.addNewAxId().setVal(id); + ctValAx.addNewAxPos(); + ctValAx.addNewScaling(); + ctValAx.addNewCrossBetween(); + ctValAx.addNewCrosses(); + ctValAx.addNewCrossAx(); + ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO); + ctValAx.addNewDelete(); + ctValAx.addNewMajorTickMark(); + ctValAx.addNewMinorTickMark(); + + setPosition(position); + setOrientation(AxisOrientation.MIN_MAX); + setCrossBetween(AxisCrossBetween.MIDPOINT_CATEGORY); + setCrosses(AxisCrosses.AUTO_ZERO); + setVisible(true); + setMajorTickMark(AxisTickMark.CROSS); + setMinorTickMark(AxisTickMark.NONE); + } +} diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java new file mode 100644 index 0000000000..fdd0e500c0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java @@ -0,0 +1,41 @@ +/* ==================================================================== + 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.text; + +import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; + +@Beta +public class XDDFTextBody { + private CTTextBody body; + + public XDDFTextBody() { + this(CTTextBody.Factory.newInstance()); + } + + @Internal + public XDDFTextBody(CTTextBody body) { + this.body = body; + } + + @Internal + public CTTextBody getXmlObject() { + return body; + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 683ceb5ce5..1f66768cde 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -38,6 +38,7 @@ import org.apache.poi.POIXMLException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.sl.usermodel.Resources; @@ -80,6 +81,7 @@ implements SlideShow { private List _slides; private List _masters; private List _pictures; + private List _charts; private XSLFTableStyles _tableStyles; private XSLFNotesMaster _notesMaster; private XSLFCommentAuthors _commentAuthors; @@ -93,7 +95,7 @@ implements SlideShow { try { if(getCorePart().getContentType().equals(XSLFRelation.THEME_MANAGER.getContentType())) { - rebase(getPackage()); + rebase(getPackage()); } //build a tree of POIXMLDocumentParts, this presentation being the root @@ -130,10 +132,16 @@ implements SlideShow { Map masterMap = new HashMap<>(); Map shIdMap = new HashMap<>(); + Map chartMap = new HashMap<>(); for (RelationPart rp : getRelationParts()) { POIXMLDocumentPart p = rp.getDocumentPart(); if (p instanceof XSLFSlide) { shIdMap.put(rp.getRelationship().getId(), (XSLFSlide) p); + for (POIXMLDocumentPart c : p.getRelations()) { + if (c instanceof XSLFChart) { + chartMap.put(c.getPackagePart().getPartName().getName(), (XSLFChart) c); + } + } } else if (p instanceof XSLFSlideMaster) { masterMap.put(getRelationId(p), (XSLFSlideMaster) p); } else if (p instanceof XSLFTableStyles){ @@ -145,6 +153,11 @@ implements SlideShow { } } + _charts = new ArrayList<>(chartMap.size()); + for(XSLFChart chart : chartMap.values()) { + _charts.add(chart); + } + _masters = new ArrayList<>(masterMap.size()); for (CTSlideMasterIdListEntry masterId : _presentation.getSldMasterIdLst().getSldMasterIdList()) { XSLFSlideMaster master = masterMap.get(masterId.getId2()); @@ -208,6 +221,7 @@ implements SlideShow { public XSLFSlide createSlide(XSLFSlideLayout layout) { int slideNumber = 256, cnt = 1; CTSlideIdList slideList; + XSLFRelation relationType = XSLFRelation.SLIDE; if (!_presentation.isSetSldIdLst()) { slideList = _presentation.addNewSldIdLst(); } else { @@ -217,35 +231,11 @@ implements SlideShow { cnt++; } - // Bug 55791: We also need to check that the resulting file name is not already taken - // this can happen when removing/adding slides - while(true) { - String slideName = XSLFRelation.SLIDE.getFileName(cnt); - boolean found = false; - for (POIXMLDocumentPart relation : getRelations()) { - if (relation.getPackagePart() != null && - slideName.equals(relation.getPackagePart().getPartName().getName())) { - // name is taken => try next one - found = true; - break; - } - } - - if(!found && - getPackage().getPartsByName(Pattern.compile(Pattern.quote(slideName))).size() > 0) { - // name is taken => try next one - found = true; - } - - if (!found) { - break; - } - cnt++; - } + cnt = findNextAvailableFileNameIndex(relationType, cnt); } - RelationPart rp = createRelationship( - XSLFRelation.SLIDE, XSLFFactory.getInstance(), cnt, false); + RelationPart rp = createRelationship + (relationType, XSLFFactory.getInstance(), cnt, false); XSLFSlide slide = rp.getDocumentPart(); CTSlideIdListEntry slideId = slideList.addNewSldId(); @@ -260,6 +250,35 @@ implements SlideShow { return slide; } + private int findNextAvailableFileNameIndex(XSLFRelation relationType, int idx) { + // Bug 55791: We also need to check that the resulting file name is not already taken + // this can happen when removing/adding slides, notes or charts + while(true) { + String fileName = relationType.getFileName(idx); + boolean found = false; + for (POIXMLDocumentPart relation : getRelations()) { + if (relation.getPackagePart() != null && + fileName.equals(relation.getPackagePart().getPartName().getName())) { + // name is taken => try next one + found = true; + break; + } + } + + if(!found && + getPackage().getPartsByName(Pattern.compile(Pattern.quote(fileName))).size() > 0) { + // name is taken => try next one + found = true; + } + + if (!found) { + break; + } + idx++; + } + return idx; + } + /** * Create a blank slide using the default (first) master. */ @@ -275,10 +294,27 @@ implements SlideShow { } layout = sl[0]; } - + return createSlide(layout); } + /** + * Create a blank chart on the given slide. + */ + public XSLFChart createChart(XSLFSlide slide) { + int chartIdx = findNextAvailableFileNameIndex(XSLFRelation.CHART, _charts.size() + 1); + XSLFChart chart = (XSLFChart) createRelationship(XSLFRelation.CHART, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart(); + slide.addRelation(null, XSLFRelation.CHART, chart); + createWorkbookRelationship(chart, chartIdx); + _charts.add(chart); + return chart; + } + + protected PackageRelationship createWorkbookRelationship(XSLFChart chart, int chartIdx) { + POIXMLDocumentPart worksheet = createRelationship(XSLFChart.WORKBOOK_RELATIONSHIP, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart(); + return chart.addRelation(null, XSLFChart.WORKBOOK_RELATIONSHIP, worksheet).getRelationship(); + } + /** * Return notes slide for the specified slide or create new if it does not exist yet. */ @@ -301,39 +337,16 @@ implements SlideShow { createNotesMaster(); } - Integer slideIndex = XSLFRelation.SLIDE.getFileNameIndex(slide); + int slideIndex = XSLFRelation.SLIDE.getFileNameIndex(slide); - // Bug 55791: We also need to check that the resulting file name is not already taken - // this can happen when removing/adding slides - while(true) { - String slideName = XSLFRelation.NOTES.getFileName(slideIndex); - boolean found = false; - for (POIXMLDocumentPart relation : getRelations()) { - if (relation.getPackagePart() != null && - slideName.equals(relation.getPackagePart().getPartName().getName())) { - // name is taken => try next one - found = true; - break; - } - } - - if(!found && - getPackage().getPartsByName(Pattern.compile(Pattern.quote(slideName))).size() > 0) { - // name is taken => try next one - found = true; - } - - if (!found) { - break; - } - slideIndex++; - } + XSLFRelation relationType = XSLFRelation.NOTES; + slideIndex = findNextAvailableFileNameIndex(relationType, slideIndex); // add notes slide to presentation XSLFNotes notesSlide = (XSLFNotes) createRelationship - (XSLFRelation.NOTES, XSLFFactory.getInstance(), slideIndex); + (relationType, XSLFFactory.getInstance(), slideIndex); // link slide and notes slide with each other - slide.addRelation(null, XSLFRelation.NOTES, notesSlide); + slide.addRelation(null, relationType, notesSlide); notesSlide.addRelation(null, XSLFRelation.NOTES_MASTER, _notesMaster); notesSlide.addRelation(null, XSLFRelation.SLIDE, slide); @@ -404,6 +417,13 @@ implements SlideShow { return _slides; } + /** + * Return all the charts in the slideshow + */ + public List getCharts() { + return _charts; + } + /** * Returns the list of comment authors, if there is one. * Will only be present if at least one slide has comments on it. @@ -444,7 +464,17 @@ implements SlideShow { public XSLFSlide removeSlide(int index){ XSLFSlide slide = _slides.remove(index); removeRelation(slide); - _presentation.getSldIdLst().removeSldId(index); + _presentation.getSldIdLst().removeSldId(index); + for (POIXMLDocumentPart p : slide.getRelations()) { + if (p instanceof XSLFChart) { + XSLFChart chart = (XSLFChart) p; + slide.removeChartRelation(chart); + _charts.remove(chart); + } else if (p instanceof XSLFSlideLayout) { + XSLFSlideLayout layout = (XSLFSlideLayout) p; + slide.removeLayoutRelation(layout); + } + } return slide; } 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 942c13bea4..30e594df8f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java @@ -26,15 +26,28 @@ import java.io.OutputStream; import javax.xml.namespace.QName; +import org.apache.poi.POIXMLDocument; import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.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.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.chart.XDDFChart; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +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.CTChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace; -import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; /** * Represents a Chart in a .pptx presentation @@ -42,63 +55,173 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; * */ @Beta -public final class XSLFChart extends POIXMLDocumentPart { +public final class XSLFChart extends XDDFChart { + protected static final POIXMLRelation WORKBOOK_RELATIONSHIP = new POIXMLRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + POIXMLDocument.PACK_OBJECT_REL_TYPE, + "/ppt/embeddings/Microsoft_Excel_Worksheet#.xlsx", + XSSFWorkbook.class + ){}; - /** - * Root element of the Chart part - */ - private CTChartSpace chartSpace; /** - * The Chart within that - */ - private CTChart chart; + * Underlying workbook + */ + private XSSFWorkbook workbook; + + + /** + * Construct a PresentationML chart. + */ + protected XSLFChart() { + super(); + } /** - * Construct a chart from a package part. + * Construct a PresentationML chart from a package part. * * @param part the package part holding the chart data, * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * + * * @since POI 3.14-Beta1 */ protected XSLFChart(PackagePart part) throws IOException, XmlException { super(part); + } - chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace(); - chart = chartSpace.getChart(); + public XSLFTextShape getTitle() { + if (!chart.isSetTitle()) { + chart.addNewTitle(); + } + final CTTitle title = chart.getTitle(); + if (title.getTx() != null && title.getTx().isSetRich()) { + return new XSLFTextShape(title, null) { + @Override + protected CTTextBody getTextBody(boolean create) { + return title.getTx().getRich(); + } + }; + } else { + return new XSLFTextShape(title, null) { + @Override + protected CTTextBody getTextBody(boolean create) { + return title.getTxPr(); + } + }; + } } - /** - * 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 chart; - } - - @Override - protected void commit() throws IOException { - XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); - xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); - - PackagePart part = getPackagePart(); - OutputStream out = part.getOutputStream(); - chartSpace.save(out, xmlOptions); - out.close(); - } + public CellReference setSheetTitle(String title) { + XSSFSheet sheet = getSheet(); + sheet.createRow(0).createCell(1).setCellValue(title); + return new CellReference(sheet.getSheetName(), 0, 1, true, true); + } + + public String formatRange(CellRangeAddress range) { + return range.formatAsString(getSheet().getSheetName(), true); + } + private XSSFSheet getSheet() { + XSSFSheet sheet = null; + try { + sheet = getWorkbook().getSheetAt(0); + } catch (InvalidFormatException ife) { + } catch (IOException ioe) { + } + return sheet; + } + private PackagePart getWorksheetPart() throws InvalidFormatException { + for (RelationPart part : getRelationParts()) { + if (WORKBOOK_RELATIONSHIP.getRelation().equals(part.getRelationship().getRelationshipType())) { + return getTargetPart(part.getRelationship()); + } + } + return null; + } + + protected 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; + } + + private XMLSlideShow getSlideShow() { + POIXMLDocumentPart p = getParent(); + while(p != null) { + if(p instanceof XMLSlideShow){ + return (XMLSlideShow)p; + } + p = p.getParent(); + } + throw new IllegalStateException("SlideShow was not found"); + } + + private PackagePart createWorksheetPart() throws InvalidFormatException { + Integer chartIdx = XSLFRelation.CHART.getFileNameIndex(this); + return getTargetPart(getSlideShow().createWorkbookRelationship(this, chartIdx)); + } + + protected void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException { + PackagePart worksheetPart = getWorksheetPart(); + if (worksheetPart == null) { + worksheetPart = createWorksheetPart(); + } + try (OutputStream xlsOut = worksheetPart.getOutputStream()) { + workbook.write(xlsOut); + } + } + + private void fillSheet(XSSFSheet sheet, XDDFDataSource categoryData, XDDFNumericalDataSource valuesData) { + int numOfPoints = categoryData.getPointCount(); + for (int i = 0; i < numOfPoints; i++) { + XSSFRow row = sheet.createRow(i + 1); // first row is for title + row.createCell(0).setCellValue(categoryData.getPointAt(i).toString()); + row.createCell(1).setCellValue(valuesData.getPointAt(i).doubleValue()); + } + } + + @Override + public void plot(XDDFChartData data) { + super.plot(data); + XSSFSheet sheet = getSheet(); + for(XDDFChartData.Series series : data.getSeries()) { + fillSheet(sheet, series.getCategoryData(), series.getValuesData()); + } + } + + public void importContent(XSLFChart other) { + this.chart.set(other.chart); + } + + @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); + } + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java index 89aae95a2f..f08e1d1de4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java @@ -20,6 +20,7 @@ package org.apache.poi.xslf.usermodel; import java.awt.geom.Rectangle2D; +import java.io.IOException; import javax.xml.namespace.QName; @@ -102,6 +103,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame { private static POILogger LOG = POILogFactory.getLogger(XSLFSheet.class); - + private XSLFDrawing _drawing; private List _shapes; private CTGroupShape _spTree; @@ -69,13 +69,13 @@ implements XSLFShapeContainer, Sheet { public XSLFSheet() { super(); } - + /** * @since POI 3.14-Beta1 */ public XSLFSheet(PackagePart part) { super(part); - } + } /** * @return the XMLSlideShow this sheet belongs to @@ -155,7 +155,7 @@ implements XSLFShapeContainer, Sheet { initDrawingAndShapes(); return _shapes; } - + /** * Helper method for initializing drawing and shapes in one go. * If they are initialized separately, there's a risk that shapes @@ -255,7 +255,7 @@ implements XSLFShapeContainer, Sheet { return sh; } - + /** * Returns an iterator over the shapes in this sheet * @@ -272,7 +272,7 @@ implements XSLFShapeContainer, Sheet { "Adding a shape from a different container is not supported -" + " create it from scratch witht XSLFSheet.create* methods"); } - + /** * Removes the specified shape from this sheet, if it is present * (optional operation). If this sheet does not contain the element, @@ -356,28 +356,35 @@ implements XSLFShapeContainer, Sheet { * @return modified 'this' */ public XSLFSheet importContent(XSLFSheet src){ - _shapes = null; - _spTree = null; - _drawing = null; _spTree = null; - _placeholders = null; - // fix-me: wth would this ever happen to work ... - - // first copy the source xml - getSpTree().set(src.getSpTree()); + getSpTree().set(src.getSpTree().copy()); + + wipeAndReinitialize(src, 0); - // recursively update each shape + return this; + } + + private void wipeAndReinitialize(XSLFSheet src, int offset) { + // explicitly initialize drawing and shapes from _spTree + _shapes = null; + _drawing = null; + initDrawingAndShapes(); + + // placeholders will be implicitly initialized when requested + _placeholders = null; + + // update each shape according to its own additional copy rules List tgtShapes = getShapes(); List srcShapes = src.getShapes(); - for(int i = 0; i < tgtShapes.size(); i++){ + for(int i = 0; i < srcShapes.size(); i++){ XSLFShape s1 = srcShapes.get(i); - XSLFShape s2 = tgtShapes.get(i); + XSLFShape s2 = tgtShapes.get(offset + i); s2.copy(s1); } - return this; + } /** @@ -387,39 +394,26 @@ implements XSLFShapeContainer, Sheet { * @return modified this. */ public XSLFSheet appendContent(XSLFSheet src){ - CTGroupShape spTree = getSpTree(); int numShapes = getShapes().size(); - + CTGroupShape spTree = getSpTree(); CTGroupShape srcTree = src.getSpTree(); + for(XmlObject ch : srcTree.selectPath("*")){ if(ch instanceof CTShape){ // simple shape - spTree.addNewSp().set(ch); + spTree.addNewSp().set(ch.copy()); } else if (ch instanceof CTGroupShape){ - spTree.addNewGrpSp().set(ch); + spTree.addNewGrpSp().set(ch.copy()); } else if (ch instanceof CTConnector){ - spTree.addNewCxnSp().set(ch); + spTree.addNewCxnSp().set(ch.copy()); } else if (ch instanceof CTPicture){ - spTree.addNewPic().set(ch); + spTree.addNewPic().set(ch.copy()); } else if (ch instanceof CTGraphicalObjectFrame){ - spTree.addNewGraphicFrame().set(ch); + spTree.addNewGraphicFrame().set(ch.copy()); } } - _shapes = null; - _spTree = null; - _drawing = null; - _spTree = null; - _placeholders = null; - - // recursively update each shape - List tgtShapes = getShapes(); - List srcShapes = src.getShapes(); - for(int i = 0; i < srcShapes.size(); i++){ - XSLFShape s1 = srcShapes.get(i); - XSLFShape s2 = tgtShapes.get(numShapes + i); + wipeAndReinitialize(src, numShapes); - s2.copy(s1); - } return this; } @@ -430,7 +424,7 @@ implements XSLFShapeContainer, Sheet { * method and return the corresponding package part. */ XSLFTheme getTheme(){ - return null; + return null; } protected XSLFTextShape getTextShapeByType(Placeholder type){ @@ -574,7 +568,7 @@ implements XSLFShapeContainer, Sheet { PackagePart pic = pictureData.getPackagePart(); RelationPart rp = addRelation(blipId, XSLFRelation.IMAGES, new XSLFPictureData(pic)); - + return rp.getRelationship().getId(); } @@ -584,13 +578,13 @@ implements XSLFShapeContainer, Sheet { PackagePart importPart(PackageRelationship srcRel, PackagePart srcPafrt) { PackagePart destPP = getPackagePart(); PackagePartName srcPPName = srcPafrt.getPartName(); - + OPCPackage pkg = destPP.getPackage(); if(pkg.containPart(srcPPName)){ // already exists return pkg.getPart(srcPPName); - } - + } + destPP.addRelationship(srcPPName, TargetMode.INTERNAL, srcRel.getRelationshipType()); PackagePart part = pkg.createPart(srcPPName, srcPafrt.getContentType()); @@ -605,7 +599,7 @@ implements XSLFShapeContainer, Sheet { } return part; } - + /** * Helper method for sheet and group shapes * @@ -614,4 +608,4 @@ implements XSLFShapeContainer, Sheet { void removePictureRelation(XSLFPictureShape pictureShape) { removeRelation(pictureShape.getBlipId()); } -} \ No newline at end of file +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java index 53b645a868..7c3d1f66cf 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -49,10 +49,10 @@ import org.xml.sax.SAXException; @Beta public final class XSLFSlide extends XSLFSheet implements Slide { - private final CTSlide _slide; - private XSLFSlideLayout _layout; - private XSLFComments _comments; - private XSLFNotes _notes; + private final CTSlide _slide; + private XSLFSlideLayout _layout; + private XSLFComments _comments; + private XSLFNotes _notes; /** * Create a new slide @@ -67,7 +67,7 @@ implements Slide { * * @param part the package part holding the slide data, * the content type must be application/vnd.openxmlformats-officedocument.slide+xml - * + * * @since POI 3.14-Beta1 */ XSLFSlide(PackagePart part) throws IOException, XmlException { @@ -79,11 +79,11 @@ implements Slide { } catch (SAXException e) { throw new IOException(e); } - + SldDocument doc = SldDocument.Factory.parse(_doc, DEFAULT_XML_OPTIONS); _slide = doc.getSld(); } - + private static CTSlide prototype(){ CTSlide ctSlide = CTSlide.Factory.newInstance(); CTCommonSlideData cSld = ctSlide.addNewCSld(); @@ -115,13 +115,21 @@ implements Slide { } @Override - public CTSlide getXmlObject() { - return _slide; - } + public CTSlide getXmlObject() { + return _slide; + } @Override protected String getRootElementName(){ - return "sld"; + return "sld"; + } + + protected void removeChartRelation(XSLFChart chart) { + removeRelation(chart); + } + + protected void removeLayoutRelation(XSLFSlideLayout layout) { + removeRelation(layout, false); } @Override @@ -131,9 +139,9 @@ implements Slide { public XSLFSlideLayout getSlideLayout(){ if(_layout == null){ - for (POIXMLDocumentPart p : getRelations()) { + for (POIXMLDocumentPart p : getRelations()) { if (p instanceof XSLFSlideLayout){ - _layout = (XSLFSlideLayout)p; + _layout = (XSLFSlideLayout)p; } } } @@ -148,36 +156,36 @@ implements Slide { } public XSLFComments getComments() { - if(_comments == null) { - for (POIXMLDocumentPart p : getRelations()) { - if (p instanceof XSLFComments) { - _comments = (XSLFComments)p; - } - } - } - if(_comments == null) { - // This slide lacks comments - // Not all have them, sorry... - return null; - } - return _comments; + if(_comments == null) { + for (POIXMLDocumentPart p : getRelations()) { + if (p instanceof XSLFComments) { + _comments = (XSLFComments)p; + } + } + } + if(_comments == null) { + // This slide lacks comments + // Not all have them, sorry... + return null; + } + return _comments; } @Override public XSLFNotes getNotes() { - if(_notes == null) { - for (POIXMLDocumentPart p : getRelations()) { - if (p instanceof XSLFNotes){ - _notes = (XSLFNotes)p; - } - } - } - if(_notes == null) { - // This slide lacks notes - // Not all have them, sorry... - return null; - } - return _notes; + if(_notes == null) { + for (POIXMLDocumentPart p : getRelations()) { + if (p instanceof XSLFNotes){ + _notes = (XSLFNotes)p; + } + } + } + if(_notes == null) { + // This slide lacks notes + // Not all have them, sorry... + return null; + } + return _notes; } @Override @@ -185,10 +193,10 @@ implements Slide { XSLFTextShape txt = getTextShapeByType(Placeholder.TITLE); return txt == null ? null : txt.getText(); } - + @Override public XSLFTheme getTheme(){ - return getSlideLayout().getSlideMaster().getTheme(); + return getSlideLayout().getSlideMaster().getTheme(); } /** @@ -241,7 +249,7 @@ implements Slide { if (bgOther == null) { return this; } - + CTBackground bgThis = _slide.getCSld().getBg(); // remove existing background if (bgThis != null) { @@ -251,14 +259,14 @@ implements Slide { } _slide.getCSld().unsetBg(); } - + bgThis = (CTBackground)_slide.getCSld().addNewBg().set(bgOther); - + if(bgOther.isSetBgPr() && bgOther.getBgPr().isSetBlipFill()){ String idOther = bgOther.getBgPr().getBlipFill().getBlip().getEmbed(); String idThis = importBlip(idOther, src.getPackagePart()); bgThis.getBgPr().getBlipFill().getBlip().setEmbed(idThis); - + } return this; @@ -294,7 +302,7 @@ implements Slide { assert(notes instanceof XSLFNotes); // TODO Auto-generated method stub } - + @Override public int getSlideNumber() { int idx = getSlideShow().getSlides().indexOf(this); diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java index 3a645f7676..dbaf370e30 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFDrawing.java @@ -19,7 +19,6 @@ package org.apache.poi.xssf.streaming; import java.util.Iterator; -import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.Drawing; @@ -53,11 +52,6 @@ public class SXSSFDrawing implements Drawing { return _drawing.createCellComment(anchor); } - @Override - public Chart createChart(ClientAnchor anchor) { - return _drawing.createChart(anchor); - } - @Override public ClientAnchor createAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) { return _drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2); 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 55f71479f7..a0a728da58 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java @@ -26,15 +26,13 @@ import java.util.List; import javax.xml.namespace.QName; -import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.charts.AxisPosition; import org.apache.poi.ss.usermodel.charts.ChartAxis; import org.apache.poi.ss.usermodel.charts.ChartAxisFactory; import org.apache.poi.ss.usermodel.charts.ChartData; -import org.apache.poi.util.Internal; import org.apache.poi.util.Removal; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xssf.usermodel.charts.XSSFCategoryAxis; import org.apache.poi.xssf.usermodel.charts.XSSFChartAxis; import org.apache.poi.xssf.usermodel.charts.XSSFChartDataFactory; @@ -46,7 +44,6 @@ import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; 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.CTPageMargins; @@ -56,7 +53,6 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrRef; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; -import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; @@ -68,139 +64,119 @@ import org.w3c.dom.Text; /** * Represents a SpreadsheetML Chart */ -public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartAxisFactory { +public final class XSSFChart extends XDDFChart implements Chart, ChartAxisFactory { - /** - * Parent graphic frame. - */ - private XSSFGraphicFrame frame; - - /** - * Root element of the SpreadsheetML Chart part - */ - private CTChartSpace chartSpace; - /** - * The Chart within that - */ - private CTChart chart; - - List axis = new ArrayList<>(); - - /** - * Create a new SpreadsheetML chart - */ - protected XSSFChart() { - super(); - createChart(); - } + /** + * Parent graphic frame. + */ + private XSSFGraphicFrame frame; - /** - * Construct a SpreadsheetML chart from a package part. - * - * @param part the package part holding the chart data, - * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * - * @since POI 3.14-Beta1 - */ - protected XSSFChart(PackagePart part) throws IOException, XmlException { - super(part); + @Deprecated + @Removal(version="4.2") + List axis = new ArrayList<>(); - chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace(); - chart = chartSpace.getChart(); - } - - /** - * Construct a new CTChartSpace bean. - * By default, it's just an empty placeholder for chart objects. - */ - private void createChart() { - chartSpace = CTChartSpace.Factory.newInstance(); - chart = chartSpace.addNewChart(); - CTPlotArea plotArea = chart.addNewPlotArea(); - - plotArea.addNewLayout(); - chart.addNewPlotVisOnly().setVal(true); - - CTPrintSettings printSettings = chartSpace.addNewPrintSettings(); - printSettings.addNewHeaderFooter(); - - CTPageMargins pageMargins = printSettings.addNewPageMargins(); - pageMargins.setB(0.75); - pageMargins.setL(0.70); - pageMargins.setR(0.70); - pageMargins.setT(0.75); - pageMargins.setHeader(0.30); - pageMargins.setFooter(0.30); - printSettings.addNewPageSetup(); - } + /** + * Create a new SpreadsheetML chart + */ + protected XSSFChart() { + super(); + createChart(); + } - /** - * Return the underlying CTChartSpace bean, the root element of the SpreadsheetML Chart part. - * - * @return the underlying CTChartSpace bean - */ - @Internal - public CTChartSpace getCTChartSpace(){ - return chartSpace; - } + /** + * Construct a SpreadsheetML chart from a package part. + * + * @param part + * the package part holding the chart data, the content type must be + * application/vnd.openxmlformats-officedocument.drawingml.chart+xml + * + * @since POI 3.14-Beta1 + */ + protected XSSFChart(PackagePart part) throws IOException, XmlException { + super(part); + } - /** - * Return the underlying CTChart bean, within the Chart Space - * - * @return the underlying CTChart bean - */ - @Internal - public CTChart getCTChart(){ - return chart; - } + /** + * Construct a new CTChartSpace bean. By default, it's just an empty placeholder for chart objects. + */ + private void createChart() { + CTPlotArea plotArea = getCTPlotArea(); + + plotArea.addNewLayout(); + chart.addNewPlotVisOnly().setVal(true); + + CTPrintSettings printSettings = chartSpace.addNewPrintSettings(); + printSettings.addNewHeaderFooter(); + + CTPageMargins pageMargins = printSettings.addNewPageMargins(); + pageMargins.setB(0.75); + pageMargins.setL(0.70); + pageMargins.setR(0.70); + pageMargins.setT(0.75); + pageMargins.setHeader(0.30); + pageMargins.setFooter(0.30); + printSettings.addNewPageSetup(); + } - @Override - protected void commit() throws IOException { - XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); - - /* - Saved chart space must have the following namespaces set: - - */ - xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); - - PackagePart part = getPackagePart(); - OutputStream out = part.getOutputStream(); - chartSpace.save(out, xmlOptions); - out.close(); - } + @Override + protected void commit() throws IOException { + XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); + + /* + * Saved chart space must have the following namespaces set: + */ + xmlOptions.setSaveSyntheticDocumentElement(new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c")); + + PackagePart part = getPackagePart(); + try (OutputStream out = part.getOutputStream()) { + chartSpace.save(out, xmlOptions); + } + } - /** - * Returns the parent graphic frame. - * @return the graphic frame this chart belongs to - */ - public XSSFGraphicFrame getGraphicFrame() { - return frame; - } + /** + * Returns the parent graphic frame. + * + * @return the graphic frame this chart belongs to + */ + public XSSFGraphicFrame getGraphicFrame() { + return frame; + } - /** - * Sets the parent graphic frame. - */ - protected void setGraphicFrame(XSSFGraphicFrame frame) { - this.frame = frame; - } + /** + * Sets the parent graphic frame. + */ + protected void setGraphicFrame(XSSFGraphicFrame frame) { + this.frame = frame; + } + @Override + @Deprecated + @Removal(version="4.2") public XSSFChartDataFactory getChartDataFactory() { return XSSFChartDataFactory.getInstance(); } + @Override + @Deprecated + @Removal(version="4.2") public XSSFChart getChartAxisFactory() { return this; } + @Override + @Deprecated + @Removal(version="4.2") public void plot(ChartData data, ChartAxis... chartAxis) { data.fillChart(this, chartAxis); } - public XSSFValueAxis createValueAxis(AxisPosition pos) { + @Override + @Deprecated + @Removal(version="4.2") + public XSSFValueAxis createValueAxis(org.apache.poi.ss.usermodel.charts.AxisPosition pos) { long id = axis.size() + 1; XSSFValueAxis valueAxis = new XSSFValueAxis(this, id, pos); if (axis.size() == 1) { @@ -212,7 +188,10 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA return valueAxis; } - public XSSFCategoryAxis createCategoryAxis(AxisPosition pos) { + @Override + @Deprecated + @Removal(version="4.2") + public XSSFCategoryAxis createCategoryAxis(org.apache.poi.ss.usermodel.charts.AxisPosition pos) { long id = axis.size() + 1; XSSFCategoryAxis categoryAxis = new XSSFCategoryAxis(this, id, pos); if (axis.size() == 1) { @@ -224,45 +203,41 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA return categoryAxis; } - public XSSFDateAxis createDateAxis(AxisPosition pos) { - long id = axis.size() + 1; - XSSFDateAxis dateAxis = new XSSFDateAxis(this, id, pos); - if (axis.size() == 1) { - ChartAxis ax = axis.get(0); - ax.crossAxis(dateAxis); - dateAxis.crossAxis(ax); - } - axis.add(dateAxis); - return dateAxis; + @Override + @Deprecated + @Removal(version="4.2") + public XSSFDateAxis createDateAxis(org.apache.poi.ss.usermodel.charts.AxisPosition pos) { + long id = axis.size() + 1; + XSSFDateAxis dateAxis = new XSSFDateAxis(this, id, pos); + if (axis.size() == 1) { + ChartAxis ax = axis.get(0); + ax.crossAxis(dateAxis); + dateAxis.crossAxis(ax); + } + axis.add(dateAxis); + return dateAxis; } - + + /** + * @deprecated use {@link getAxes} instead + */ + @Override + @Deprecated + @Removal(version="4.2") public List getAxis() { if (axis.isEmpty() && hasAxis()) { parseAxis(); } return axis; } - + + @Override + @Deprecated + @Removal(version="4.2") public XSSFManualLayout getManualLayout() { return new XSSFManualLayout(this); } - /** - * @return true if only visible cells will be present on the chart, - * false otherwise - */ - public boolean isPlotOnlyVisibleCells() { - return chart.getPlotVisOnly().getVal(); - } - - /** - * @param plotVisOnly a flag specifying if only visible cells should be - * present on the chart - */ - public void setPlotOnlyVisibleCells(boolean plotVisOnly) { - chart.getPlotVisOnly().setVal(plotVisOnly); - } - /** * Returns the title static text, or null if none is set. * Note that a title formula may be set instead. @@ -274,14 +249,14 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA public XSSFRichTextString getTitle() { return getTitleText(); } - + /** * Returns the title static text, or null if none is set. * Note that a title formula may be set instead. * Empty text result is for backward compatibility, and could mean the title text is empty or there is a formula instead. * Check for a formula first, falling back on text for cleaner logic. - * @return static title text if set, - * null if there is no title, + * @return static title text if set, + * null if there is no title, * empty string if the title text is empty or the title uses a formula instead */ public XSSFRichTextString getTitleText() { @@ -295,8 +270,8 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA StringBuilder text = new StringBuilder(64); XmlObject[] t = title .selectPath("declare namespace a='"+XSSFDrawing.NAMESPACE_A+"' .//a:t"); - for (int m = 0; m < t.length; m++) { - NodeList kids = t[m].getDomNode().getChildNodes(); + for (XmlObject element : t) { + NodeList kids = element.getDomNode().getChildNodes(); final int count = kids.getLength(); for (int n = 0; n < count; n++) { Node kid = kids.item(n); @@ -315,161 +290,169 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA * @deprecated POI 3.16, use {@link #setTitleText(String)} instead. */ @Deprecated - @Removal(version="4.0") - public void setTitle(String newTitle) { - - } - + @Removal(version = "4.0") + public void setTitle(String newTitle) { + + } + /** * Sets the title text as a static string. - * @param newTitle to use + * + * @param newTitle + * to use */ - public void setTitleText(String newTitle) { - CTTitle ctTitle; - if (chart.isSetTitle()) { - ctTitle = chart.getTitle(); - } else { - ctTitle = chart.addNewTitle(); - } + public void setTitleText(String newTitle) { + CTTitle ctTitle; + if (chart.isSetTitle()) { + ctTitle = chart.getTitle(); + } else { + ctTitle = chart.addNewTitle(); + } - CTTx tx; - if (ctTitle.isSetTx()) { - tx = ctTitle.getTx(); - } else { - tx = ctTitle.addNewTx(); - } + CTTx tx; + if (ctTitle.isSetTx()) { + tx = ctTitle.getTx(); + } else { + tx = ctTitle.addNewTx(); + } - if (tx.isSetStrRef()) { - tx.unsetStrRef(); - } + if (tx.isSetStrRef()) { + tx.unsetStrRef(); + } - CTTextBody rich; - if (tx.isSetRich()) { - rich = tx.getRich(); - } else { - rich = tx.addNewRich(); - rich.addNewBodyPr(); // body properties must exist (but can be empty) - } + CTTextBody rich; + if (tx.isSetRich()) { + rich = tx.getRich(); + } else { + rich = tx.addNewRich(); + rich.addNewBodyPr(); // body properties must exist (but can be + // empty) + } - CTTextParagraph para; - if (rich.sizeOfPArray() > 0) { - para = rich.getPArray(0); - } else { - para = rich.addNewP(); - } + CTTextParagraph para; + if (rich.sizeOfPArray() > 0) { + para = rich.getPArray(0); + } else { + para = rich.addNewP(); + } - if (para.sizeOfRArray() > 0) { - CTRegularTextRun run = para.getRArray(0); - run.setT(newTitle); - } else if (para.sizeOfFldArray() > 0) { - CTTextField fld = para.getFldArray(0); - fld.setT(newTitle); - } else { - CTRegularTextRun run = para.addNewR(); - run.setT(newTitle); - } - } - - /** - * Get the chart title formula expression if there is one - * @return formula expression or null - */ - public String getTitleFormula() { - if(! chart.isSetTitle()) { - return null; - } - - CTTitle title = chart.getTitle(); - - if (! title.isSetTx()) { - return null; - } - - CTTx tx = title.getTx(); - - if (! tx.isSetStrRef()) { - return null; - } - - return tx.getStrRef().getF(); - } - - /** - * Set the formula expression to use for the chart title - * @param formula - */ - public void setTitleFormula(String formula) { - CTTitle ctTitle; - if (chart.isSetTitle()) { - ctTitle = chart.getTitle(); - } else { - ctTitle = chart.addNewTitle(); - } - - CTTx tx; - if (ctTitle.isSetTx()) { - tx = ctTitle.getTx(); - } else { - tx = ctTitle.addNewTx(); - } - - if (tx.isSetRich()) { - tx.unsetRich(); - } - - CTStrRef strRef; - if (tx.isSetStrRef()) { - strRef = tx.getStrRef(); - } else { - strRef = tx.addNewStrRef(); - } - - strRef.setF(formula); - } + if (para.sizeOfRArray() > 0) { + CTRegularTextRun run = para.getRArray(0); + run.setT(newTitle); + } else if (para.sizeOfFldArray() > 0) { + CTTextField fld = para.getFldArray(0); + fld.setT(newTitle); + } else { + CTRegularTextRun run = para.addNewR(); + run.setT(newTitle); + } + } + + /** + * Get the chart title formula expression if there is one + * + * @return formula expression or null + */ + public String getTitleFormula() { + if (!chart.isSetTitle()) { + return null; + } + + CTTitle title = chart.getTitle(); + + if (!title.isSetTx()) { + return null; + } + + CTTx tx = title.getTx(); + + if (!tx.isSetStrRef()) { + return null; + } + return tx.getStrRef().getF(); + } + + /** + * Set the formula expression to use for the chart title + * + * @param formula + */ + public void setTitleFormula(String formula) { + CTTitle ctTitle; + if (chart.isSetTitle()) { + ctTitle = chart.getTitle(); + } else { + ctTitle = chart.addNewTitle(); + } + + CTTx tx; + if (ctTitle.isSetTx()) { + tx = ctTitle.getTx(); + } else { + tx = ctTitle.addNewTx(); + } + + if (tx.isSetRich()) { + tx.unsetRich(); + } + + CTStrRef strRef; + if (tx.isSetStrRef()) { + strRef = tx.getStrRef(); + } else { + strRef = tx.addNewStrRef(); + } + + strRef.setF(formula); + } + + @Override + @Deprecated + @Removal(version="4.2") public XSSFChartLegend getOrCreateLegend() { return new XSSFChartLegend(this); } - public void deleteLegend() { - if (chart.isSetLegend()) { - chart.unsetLegend(); - } - } - + @Deprecated + @Removal(version="4.2") private boolean hasAxis() { - CTPlotArea ctPlotArea = chart.getPlotArea(); - int totalAxisCount = - ctPlotArea.sizeOfValAxArray() + - ctPlotArea.sizeOfCatAxArray() + - ctPlotArea.sizeOfDateAxArray() + - ctPlotArea.sizeOfSerAxArray(); - return totalAxisCount > 0; + CTPlotArea ctPlotArea = chart.getPlotArea(); + int totalAxisCount = ctPlotArea.sizeOfValAxArray() + ctPlotArea.sizeOfCatAxArray() + ctPlotArea.sizeOfDateAxArray() + ctPlotArea.sizeOfSerAxArray(); + return totalAxisCount > 0; } - private void parseAxis() { - // TODO: add other axis types - parseCategoryAxis(); - parseDateAxis(); - parseValueAxis(); - } + @Deprecated + @Removal(version="4.2") + private void parseAxis() { + // TODO: add other axis types + parseCategoryAxis(); + parseDateAxis(); + parseValueAxis(); + } - private void parseCategoryAxis() { - for (CTCatAx catAx : chart.getPlotArea().getCatAxArray()) { - axis.add(new XSSFCategoryAxis(this, catAx)); - } - } + @Deprecated + @Removal(version="4.2") + private void parseCategoryAxis() { + for (CTCatAx catAx : chart.getPlotArea().getCatAxArray()) { + axis.add(new XSSFCategoryAxis(this, catAx)); + } + } - private void parseDateAxis() { - for (CTDateAx dateAx : chart.getPlotArea().getDateAxArray()) { - axis.add(new XSSFDateAxis(this, dateAx)); - } - } - - private void parseValueAxis() { - for (CTValAx valAx : chart.getPlotArea().getValAxArray()) { - axis.add(new XSSFValueAxis(this, valAx)); - } - } + @Deprecated + @Removal(version="4.2") + private void parseDateAxis() { + for (CTDateAx dateAx : chart.getPlotArea().getDateAxArray()) { + axis.add(new XSSFDateAxis(this, dateAx)); + } + } + + @Deprecated + @Removal(version="4.2") + private void parseValueAxis() { + for (CTValAx valAx : chart.getPlotArea().getValAxArray()) { + axis.add(new XSSFValueAxis(this, valAx)); + } + } } 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 5c88adb316..0bcc8ec168 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -239,7 +239,12 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing series; public XSSFLineChartData() { - series = new ArrayList<>(); + series = new ArrayList(); } static class Series extends AbstractXSSFChartSeries implements LineChartSeries { @@ -64,10 +68,12 @@ public class XSSFLineChartData implements LineChartData { this.values = values; } + @Override public ChartDataSource getCategoryAxisData() { return categories; } + @Override public ChartDataSource getValues() { return values; } @@ -91,6 +97,7 @@ public class XSSFLineChartData implements LineChartData { } } + @Override public LineChartSeries addSeries(ChartDataSource categoryAxisData, ChartDataSource values) { if (!values.isNumeric()) { throw new IllegalArgumentException("Value data source must be numeric."); @@ -101,10 +108,12 @@ public class XSSFLineChartData implements LineChartData { return newSeries; } + @Override public List getSeries() { return series; } + @Override public void fillChart(Chart chart, ChartAxis... axis) { if (!(chart instanceof XSSFChart)) { throw new IllegalArgumentException("Chart must be instance of XSSFChart"); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFManualLayout.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFManualLayout.java index 76eaa45f9d..1f5b66b43c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFManualLayout.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFManualLayout.java @@ -17,25 +17,27 @@ package org.apache.poi.xssf.usermodel.charts; -import org.apache.poi.util.Beta; -import org.apache.poi.util.Internal; -import org.apache.poi.ss.usermodel.charts.ManualLayout; import org.apache.poi.ss.usermodel.charts.LayoutMode; import org.apache.poi.ss.usermodel.charts.LayoutTarget; +import org.apache.poi.ss.usermodel.charts.ManualLayout; +import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; +import org.apache.poi.xddf.usermodel.chart.XDDFManualLayout; import org.apache.poi.xssf.usermodel.XSSFChart; -import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget; import org.openxmlformats.schemas.drawingml.x2006.chart.CTLayout; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTManualLayout; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; import org.openxmlformats.schemas.drawingml.x2006.chart.CTLayoutMode; import org.openxmlformats.schemas.drawingml.x2006.chart.CTLayoutTarget; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTManualLayout; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutMode; +import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget; /** * Represents a SpreadsheetML manual layout. - * @author Roman Kashitsyn + * @deprecated use {@link XDDFManualLayout instead} */ -@Beta +@Deprecated +@Removal(version="4.2") public final class XSSFManualLayout implements ManualLayout { /** @@ -75,126 +77,144 @@ public final class XSSFManualLayout implements ManualLayout { return layout; } - public void setWidthRatio(double ratio) { + @Override + public void setWidthRatio(double ratio) { if (!layout.isSetW()) { layout.addNewW(); } layout.getW().setVal(ratio); } - public double getWidthRatio() { + @Override + public double getWidthRatio() { if (!layout.isSetW()) { return 0.0; } return layout.getW().getVal(); } - public void setHeightRatio(double ratio) { + @Override + public void setHeightRatio(double ratio) { if (!layout.isSetH()) { layout.addNewH(); } layout.getH().setVal(ratio); } - public double getHeightRatio() { + @Override + public double getHeightRatio() { if (!layout.isSetH()) { return 0.0; } return layout.getH().getVal(); } - public LayoutTarget getTarget() { + @Override + public LayoutTarget getTarget() { if (!layout.isSetLayoutTarget()) { return defaultLayoutTarget; } return toLayoutTarget(layout.getLayoutTarget()); } - public void setTarget(LayoutTarget target) { + @Override + public void setTarget(LayoutTarget target) { if (!layout.isSetLayoutTarget()) { layout.addNewLayoutTarget(); } layout.getLayoutTarget().setVal(fromLayoutTarget(target)); } - public LayoutMode getXMode() { + @Override + public LayoutMode getXMode() { if (!layout.isSetXMode()) { return defaultLayoutMode; } return toLayoutMode(layout.getXMode()); } - public void setXMode(LayoutMode mode) { + @Override + public void setXMode(LayoutMode mode) { if (!layout.isSetXMode()) { layout.addNewXMode(); } layout.getXMode().setVal(fromLayoutMode(mode)); } - public LayoutMode getYMode() { + @Override + public LayoutMode getYMode() { if (!layout.isSetYMode()) { return defaultLayoutMode; } return toLayoutMode(layout.getYMode()); } - public void setYMode(LayoutMode mode) { + @Override + public void setYMode(LayoutMode mode) { if (!layout.isSetYMode()) { layout.addNewYMode(); } layout.getYMode().setVal(fromLayoutMode(mode)); } - public double getX() { + @Override + public double getX() { if (!layout.isSetX()) { return 0.0; } return layout.getX().getVal(); } - public void setX(double x) { + @Override + public void setX(double x) { if (!layout.isSetX()) { layout.addNewX(); } layout.getX().setVal(x); } - public double getY() { + @Override + public double getY() { if (!layout.isSetY()) { return 0.0; } return layout.getY().getVal(); } - public void setY(double y) { + @Override + public void setY(double y) { if (!layout.isSetY()) { layout.addNewY(); } layout.getY().setVal(y); } - public LayoutMode getWidthMode() { + @Override + public LayoutMode getWidthMode() { if (!layout.isSetWMode()) { return defaultLayoutMode; } return toLayoutMode(layout.getWMode()); } - public void setWidthMode(LayoutMode mode) { + @Override + public void setWidthMode(LayoutMode mode) { if (!layout.isSetWMode()) { layout.addNewWMode(); } layout.getWMode().setVal(fromLayoutMode(mode)); } - public LayoutMode getHeightMode() { + @Override + public LayoutMode getHeightMode() { if (!layout.isSetHMode()) { return defaultLayoutMode; } return toLayoutMode(layout.getHMode()); } - public void setHeightMode(LayoutMode mode) { + @Override + public void setHeightMode(LayoutMode mode) { if (!layout.isSetHMode()) { layout.addNewHMode(); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFScatterChartData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFScatterChartData.java index b192c36bd9..98d9f8fd10 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFScatterChartData.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFScatterChartData.java @@ -25,7 +25,8 @@ import org.apache.poi.ss.usermodel.charts.ChartAxis; import org.apache.poi.ss.usermodel.charts.ChartDataSource; import org.apache.poi.ss.usermodel.charts.ScatterChartData; import org.apache.poi.ss.usermodel.charts.ScatterChartSeries; -import org.apache.poi.util.Beta; +import org.apache.poi.util.Removal; +import org.apache.poi.xddf.usermodel.chart.XDDFScatterChartData; import org.apache.poi.xssf.usermodel.XSSFChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; @@ -38,8 +39,11 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STScatterStyle; /** * Represents DrawingML scatter charts. + * + * @deprecated use {@link XDDFScatterChartData} instead */ -@Beta +@Deprecated +@Removal(version="4.2") public class XSSFScatterChartData implements ScatterChartData { /** @@ -48,7 +52,7 @@ public class XSSFScatterChartData implements ScatterChartData { private List series; public XSSFScatterChartData() { - series = new ArrayList<>(); + series = new ArrayList(); } /** @@ -74,6 +78,7 @@ public class XSSFScatterChartData implements ScatterChartData { * Returns data source used for X axis values. * @return data source used for X axis values */ + @Override public ChartDataSource getXValues() { return xs; } @@ -82,6 +87,7 @@ public class XSSFScatterChartData implements ScatterChartData { * Returns data source used for Y axis values. * @return data source used for Y axis values */ + @Override public ChartDataSource getYValues() { return ys; } @@ -103,6 +109,7 @@ public class XSSFScatterChartData implements ScatterChartData { } } + @Override public ScatterChartSeries addSerie(ChartDataSource xs, ChartDataSource ys) { if (!ys.isNumeric()) { @@ -114,6 +121,7 @@ public class XSSFScatterChartData implements ScatterChartData { return newSerie; } + @Override public void fillChart(Chart chart, ChartAxis... axis) { if (!(chart instanceof XSSFChart)) { throw new IllegalArgumentException("Chart must be instance of XSSFChart"); @@ -133,6 +141,7 @@ public class XSSFScatterChartData implements ScatterChartData { } } + @Override public List getSeries() { return series; } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFValueAxis.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFValueAxis.java index 3dea67ddb4..14f16d4a11 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFValueAxis.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFValueAxis.java @@ -24,8 +24,9 @@ import org.apache.poi.ss.usermodel.charts.AxisPosition; import org.apache.poi.ss.usermodel.charts.AxisTickMark; import org.apache.poi.ss.usermodel.charts.ChartAxis; import org.apache.poi.ss.usermodel.charts.ValueAxis; -import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; +import org.apache.poi.util.Removal; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xssf.usermodel.XSSFChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean; @@ -42,9 +43,10 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; /** * Value axis type. * - * @author Roman Kashitsyn + * @deprecated use {@link XDDFValueAxis} instead */ -@Beta +@Deprecated +@Removal(version="4.2") public class XSSFValueAxis extends XSSFChartAxis implements ValueAxis { private CTValAx ctValAx; diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java index e7df6ec69f..dbc68c2155 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java @@ -22,39 +22,23 @@ import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; import javax.xml.namespace.QName; -import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; -import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; -import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.poi.util.IOUtils; -import org.apache.poi.util.Internal; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace; -import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; /** * Represents a Chart in a .docx file */ @Beta -public class XWPFChart extends POIXMLDocumentPart { - - /** - * Root element of the Chart part - */ - private final CTChartSpace chartSpace; - - /** - * The Chart within that - */ - private final CTChart chart; +public class XWPFChart extends XDDFChart { // lazy initialization private Long checksum; @@ -64,39 +48,11 @@ public class XWPFChart extends POIXMLDocumentPart { * * @param part the package part holding the chart data, * the content type must be application/vnd.openxmlformats-officedocument.drawingml.chart+xml - * + * * @since POI 4.0.0 */ protected XWPFChart(PackagePart part) throws IOException, XmlException { super(part); - - chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace(); - chart = chartSpace.getChart(); - } - - @Override - protected void onDocumentRead() throws IOException { - super.onDocumentRead(); - } - - /** - * 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 chart; } @Override @@ -108,7 +64,7 @@ public class XWPFChart extends POIXMLDocumentPart { chartSpace.save(out, xmlOptions); } } - + public Long getChecksum() { if (this.checksum == null) { InputStream is = null; @@ -120,7 +76,9 @@ public class XWPFChart extends POIXMLDocumentPart { throw new POIXMLException(e); } finally { try { - if (is != null) is.close(); + if (is != null) { + is.close(); + } } catch (IOException e) { throw new POIXMLException(e); } @@ -157,7 +115,7 @@ public class XWPFChart extends POIXMLDocumentPart { } return false; } - + @Override public int hashCode() { return getChecksum().hashCode(); diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index e63b98995d..2b3c3eda7b 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -63,7 +63,27 @@ import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtBlock; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSectPr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CommentsDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.EndnotesDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.FootnotesDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.NumberingDocument; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STDocProtect; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STHdrFtr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; /** *

High(ish) level class for working with .docx files.

@@ -79,7 +99,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; */ public class XWPFDocument extends POIXMLDocument implements Document, IBody { private static final POILogger LOG = POILogFactory.getLogger(XWPFDocument.class); - + protected List footers = new ArrayList<>(); protected List headers = new ArrayList<>(); protected List comments = new ArrayList<>(); @@ -186,8 +206,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } docCursor.dispose(); // Sort out headers and footers - if (doc.getDocument().getBody().getSectPr() != null) + if (doc.getDocument().getBody().getSectPr() != null) { headerFooterPolicy = new XWPFHeaderFooterPolicy(this); + } // Create for each XML-part in the Package a PartClass for (RelationPart rp : getRelationParts()) { @@ -224,7 +245,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } else if (relation.equals(XWPFRelation.CHART.getRelation())) { //now we can use all methods to modify charts in XWPFDocument XWPFChart chartData = (XWPFChart) p; - chartData.onDocumentRead(); +// chartData.onDocumentRead(); // ??? there is nothing to be done there!!! charts.add(chartData); } else if (relation.equals(XWPFRelation.GLOSSARY_DOCUMENT.getRelation())) { // We don't currently process the glossary itself @@ -380,20 +401,25 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFHyperlink getHyperlinkByID(String id) { for (XWPFHyperlink link : hyperlinks) { - if (link.getId().equals(id)) + if (link.getId().equals(id)) { return link; + } } return null; } public XWPFFootnote getFootnoteByID(int id) { - if (footnotes == null) return null; + if (footnotes == null) { + return null; + } return footnotes.getFootnoteById(id); } public XWPFFootnote getEndnoteByID(int id) { - if (endnotes == null) return null; + if (endnotes == null) { + return null; + } return endnotes.get(id); } @@ -410,8 +436,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public XWPFComment getCommentByID(String id) { for (XWPFComment comment : comments) { - if (comment.getId().equals(id)) + if (comment.getId().equals(id)) { return comment; + } } return null; @@ -450,7 +477,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } return headerFooterPolicy; } - + /** * Create a header of the given type * @@ -471,8 +498,8 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt())); } - - + + /** * Create a footer of the given type * @@ -619,7 +646,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { String uri = CTP.type.getName().getNamespaceURI(); /* * TODO DO not use a coded constant, find the constant in the OOXML - * classes instead, as the child of type CT_Paragraph is defined in the + * classes instead, as the child of type CT_Paragraph is defined in the * OOXML schema as 'p' */ String localPart = "p"; @@ -667,8 +694,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { cursor.toCursor(newParaPos); while (cursor.toPrevSibling()) { o = cursor.getObject(); - if (o instanceof CTP || o instanceof CTTbl) + if (o instanceof CTP || o instanceof CTTbl) { i++; + } } bodyElements.add(i, newP); cursor.toCursor(newParaPos); @@ -706,8 +734,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { cursor.toCursor(tableCursor); while (cursor.toPrevSibling()) { o = cursor.getObject(); - if (o instanceof CTP || o instanceof CTTbl) + if (o instanceof CTP || o instanceof CTTbl) { i++; + } } bodyElements.add(i, newT); cursor.toCursor(tableCursor); @@ -984,7 +1013,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { tables.set(pos, table); ctDocument.getBody().setTblArray(pos, table.getCTTbl()); } - + /** * Verifies that the documentProtection tag in settings.xml file
* specifies that the protection is enforced (w:enforcement="1")
@@ -1288,7 +1317,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { public void setZoomPercent(long zoomPercent) { settings.setZoomPercent(zoomPercent); } - + /** * inserts an existing XWPFTable to the arrays bodyElements and tables * @@ -1378,7 +1407,9 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { throw new POIXMLException(e); } finally { try { - if (out != null) out.close(); + if (out != null) { + out.close(); + } } catch (IOException e) { // ignore } diff --git a/src/ooxml/testcases/org/apache/poi/xddf/usermodel/chart/TestXDDFDataSourcesFactory.java b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/chart/TestXDDFDataSourcesFactory.java new file mode 100644 index 0000000000..f6c547cb15 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/chart/TestXDDFDataSourcesFactory.java @@ -0,0 +1,134 @@ +/* ==================================================================== + 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 org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.SheetBuilder; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import junit.framework.TestCase; + +/** + * Tests for {@link XDDFDataSourcesFactory}. + */ +public class TestXDDFDataSourcesFactory extends TestCase { + + private static final Object[][] numericCells = { + {0.0, 1.0, 2.0, 3.0, 4.0}, + {0.0, "=B1*2", "=C1*2", "=D1*2", "=E1*2"} + }; + + private static final Object[][] stringCells = { + { 1, 2, 3, 4, 5}, + {"A", "B", "C", "D", "E"} + }; + + private static final Object[][] mixedCells = { + {1.0, "2.0", 3.0, "4.0", 5.0, "6.0"} + }; + + public void testNumericArrayDataSource() { + Double[] doubles = new Double[]{1.0, 2.0, 3.0, 4.0, 5.0}; + XDDFDataSource doubleDataSource = XDDFDataSourcesFactory.fromArray(doubles, null); + assertTrue(doubleDataSource.isNumeric()); + assertFalse(doubleDataSource.isReference()); + assertDataSourceIsEqualToArray(doubleDataSource, doubles); + } + + public void testStringArrayDataSource() { + String[] strings = new String[]{"one", "two", "three", "four", "five"}; + XDDFDataSource stringDataSource = XDDFDataSourcesFactory.fromArray(strings, null); + assertFalse(stringDataSource.isNumeric()); + assertFalse(stringDataSource.isReference()); + assertDataSourceIsEqualToArray(stringDataSource, strings); + } + + public void testNumericCellDataSource() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, numericCells).build(); + CellRangeAddress numCellRange = CellRangeAddress.valueOf("A2:E2"); + XDDFDataSource numDataSource = XDDFDataSourcesFactory.fromNumericCellRange(sheet, numCellRange); + assertTrue(numDataSource.isReference()); + assertTrue(numDataSource.isNumeric()); + assertEquals(numericCells[0].length, numDataSource.getPointCount()); + for (int i = 0; i < numericCells[0].length; ++i) { + assertEquals(((Double) numericCells[0][i]) * 2, + numDataSource.getPointAt(i), 0.00001); + } + } + + public void testStringCellDataSource() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, stringCells).build(); + CellRangeAddress numCellRange = CellRangeAddress.valueOf("A2:E2"); + XDDFDataSource numDataSource = XDDFDataSourcesFactory.fromStringCellRange(sheet, numCellRange); + assertTrue(numDataSource.isReference()); + assertFalse(numDataSource.isNumeric()); + assertEquals(numericCells[0].length, numDataSource.getPointCount()); + for (int i = 0; i < stringCells[1].length; ++i) { + assertEquals(stringCells[1][i], numDataSource.getPointAt(i)); + } + } + + public void testMixedCellDataSource() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, mixedCells).build(); + CellRangeAddress mixedCellRange = CellRangeAddress.valueOf("A1:F1"); + XDDFDataSource strDataSource = XDDFDataSourcesFactory.fromStringCellRange(sheet, mixedCellRange); + XDDFDataSource numDataSource = XDDFDataSourcesFactory.fromNumericCellRange(sheet, mixedCellRange); + for (int i = 0; i < mixedCells[0].length; ++i) { + if (i % 2 == 0) { + assertNull(strDataSource.getPointAt(i)); + assertEquals(((Double) mixedCells[0][i]), + numDataSource.getPointAt(i), 0.00001); + } else { + assertNull(numDataSource.getPointAt(i)); + assertEquals(mixedCells[0][i], strDataSource.getPointAt(i)); + } + } + } + + public void testIOBExceptionOnInvalidIndex() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, numericCells).build(); + CellRangeAddress rangeAddress = CellRangeAddress.valueOf("A2:E2"); + XDDFDataSource numDataSource = XDDFDataSourcesFactory.fromNumericCellRange(sheet, rangeAddress); + IndexOutOfBoundsException exception = null; + try { + numDataSource.getPointAt(-1); + } catch (IndexOutOfBoundsException e) { + exception = e; + } + assertNotNull(exception); + + exception = null; + try { + numDataSource.getPointAt(numDataSource.getPointCount()); + } catch (IndexOutOfBoundsException e) { + exception = e; + } + assertNotNull(exception); + } + + private void assertDataSourceIsEqualToArray(XDDFDataSource ds, T[] array) { + assertEquals(ds.getPointCount(), array.length); + for (int i = 0; i < array.length; ++i) { + assertEquals(ds.getPointAt(i), array[i]); + } + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFChart.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFChart.java index e5f531dd38..5a66e41f8d 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFChart.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFChart.java @@ -18,44 +18,151 @@ */ package org.apache.poi.xslf.usermodel; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.io.IOException; -import java.io.OutputStream; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.List; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xddf.usermodel.chart.AxisCrossBetween; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisOrientation; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.AxisTickMark; +import org.apache.poi.xddf.usermodel.chart.BarDirection; +import org.apache.poi.xddf.usermodel.chart.BarGrouping; +import org.apache.poi.xddf.usermodel.chart.Grouping; +import org.apache.poi.xddf.usermodel.chart.LayoutMode; +import org.apache.poi.xddf.usermodel.chart.LayoutTarget; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.RadarStyle; +import org.apache.poi.xddf.usermodel.chart.ScatterStyle; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFLineChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFManualLayout; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFRadarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFScatterChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xslf.XSLFTestDataSamples; -import org.apache.poi.xssf.usermodel.XSSFRow; -import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerTx; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrData; -import org.openxmlformats.schemas.drawingml.x2006.chart.CTStrVal; +/** + * a modified version from POI-examples + */ public class TestXSLFChart { - - /** - * a modified version from POI-examples - */ @Test - public void testFillChartTemplate() throws IOException { + public void testFillPieChartTemplate() throws IOException { + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("pie-chart.pptx"); + XSLFChart chart = findChart(pptx.getSlides().get(0)); + List data = findChartData(chart); - String chartTitle = "Apache POI"; // first line is chart title + XDDFPieChartData pie = (XDDFPieChartData) data.get(0); + XDDFPieChartData.Series firstSeries = (XDDFPieChartData.Series) pie.getSeries().get(0); + firstSeries.setExplosion(25); + assertEquals(25, firstSeries.getExplosion()); - XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("pie-chart.pptx"); + fillChartData(chart, pie); + pptx.close(); + } + + @Test + public void testFillBarChartTemplate() throws IOException { + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("bar-chart.pptx"); XSLFSlide slide = pptx.getSlides().get(0); + // duplicate slide and chart before applying "destructive" tests to it + XSLFChart chart2 = findChart(pptx.createSlide().importContent(slide)); + XSLFChart chart = findChart(slide); + + List data = findChartData(chart); + XDDFBarChartData bar = (XDDFBarChartData) data.get(0); + assertEquals(BarDirection.BAR, bar.getBarDirection()); + assertEquals(BarGrouping.CLUSTERED, bar.getBarGrouping()); + assertEquals(100, bar.getGapWidth()); + fillChartData(chart, bar); + + XDDFBarChartData column = (XDDFBarChartData) findChartData(chart2).get(0); + column.setBarDirection(BarDirection.COL); + assertEquals(BarDirection.COL, column.getBarDirection()); + column.getCategoryAxis().setOrientation(AxisOrientation.MIN_MAX); + column.getValueAxes().get(0).setPosition(AxisPosition.BOTTOM); + fillChartData(chart2, column); + + pptx.close(); + } + + @Test + public void testFillLineChartTemplate() throws IOException { + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("line-chart.pptx"); + XSLFChart chart = findChart(pptx.getSlides().get(0)); + List data = findChartData(chart); + + XDDFLineChartData line = (XDDFLineChartData) data.get(0); + assertEquals(Grouping.STANDARD, line.getGrouping()); + line.setGrouping(Grouping.PERCENT_STACKED); + assertEquals(Grouping.PERCENT_STACKED, line.getGrouping()); + + fillChartData(chart, line); + pptx.close(); + } + + @Test + public void testFillRadarChartTemplate() throws IOException { + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("radar-chart.pptx"); + XSLFChart chart = findChart(pptx.getSlides().get(0)); + List data = findChartData(chart); + + XDDFRadarChartData radar = (XDDFRadarChartData) data.get(0); + assertEquals(RadarStyle.MARKER, radar.getStyle()); + radar.setStyle(RadarStyle.FILLED); + assertEquals(RadarStyle.FILLED, radar.getStyle()); + + fillChartData(chart, radar); + pptx.close(); + } + + @Test + public void testFillScatterChartTemplate() throws IOException { + XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument("scatter-chart.pptx"); + XSLFChart chart = findChart(pptx.getSlides().get(0)); + List data = findChartData(chart); + + XDDFScatterChartData scatter = (XDDFScatterChartData) data.get(0); + assertEquals(ScatterStyle.LINE_MARKER, scatter.getStyle()); + scatter.setStyle(ScatterStyle.SMOOTH); + assertEquals(ScatterStyle.SMOOTH, scatter.getStyle()); + + fillChartData(chart, scatter); + pptx.close(); + } + + private void fillChartData(XSLFChart chart, XDDFChartData data) { + final int numOfPoints = 3; + final String[] categories = {"First", "Second", "Third"}; + final Integer[] values = {1, 3, 4}; + + final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); + final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); + final XDDFChartData.Series series = data.getSeries().get(0); + final XDDFDataSource categoryData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange); + final XDDFNumericalDataSource valuesData = XDDFDataSourcesFactory.fromArray(values, valuesDataRange); + series.replaceData(categoryData, valuesData); + final String title = "Apache POI"; + series.setTitle(title, chart.setSheetTitle(title)); + chart.plot(data); + } + + private XSLFChart findChart(XSLFSlide slide) { // find chart in the slide XSLFChart chart = null; for(POIXMLDocumentPart part : slide.getRelations()){ @@ -65,76 +172,161 @@ public class TestXSLFChart { } } - if(chart == null) throw new IllegalStateException("chart not found in the template"); + if(chart == null) { + throw new IllegalStateException("chart not found in the template"); + } - // embedded Excel workbook that holds the chart data - POIXMLDocumentPart xlsPart = chart.getRelations().get(0); - XSSFWorkbook wb = new XSSFWorkbook(); - XSSFSheet sheet = wb.createSheet(); + checkLegendOperations(chart); + return chart; + } - CTChart ctChart = chart.getCTChart(); - CTPlotArea plotArea = ctChart.getPlotArea(); + private List findChartData(XSLFChart chart) { + List data = chart.getChartSeries(); + assertNotNull(data); + assertEquals(1, data.size()); - CTPieChart pieChart = plotArea.getPieChartArray(0); - //Pie Chart Series - CTPieSer ser = pieChart.getSerArray(0); + XDDFChartData firstSeries = data.get(0); + assertNotNull(firstSeries); + if (firstSeries instanceof XDDFScatterChartData) { + assertEquals(null, firstSeries.getCategoryAxis()); + assertEquals(2, firstSeries.getValueAxes().size()); + checkAxisOperations(firstSeries.getValueAxes().get(0)); + checkAxisOperations(firstSeries.getValueAxes().get(1)); + } else if (!(firstSeries instanceof XDDFPieChartData)) { + assertNotNull(firstSeries.getCategoryAxis()); + assertEquals(1, firstSeries.getValueAxes().size()); + checkAxisOperations(firstSeries.getValueAxes().get(0)); + } - // Series Text - CTSerTx tx = ser.getTx(); - tx.getStrRef().getStrCache().getPtArray(0).setV(chartTitle); - sheet.createRow(0).createCell(1).setCellValue(chartTitle); - String titleRef = new CellReference(sheet.getSheetName(), 0, 1, true, true).formatAsString(); - tx.getStrRef().setF(titleRef); + return data; + } + private void checkLegendOperations(XSLFChart chart) { + XDDFChartLegend legend = chart.getOrAddLegend(); + assertFalse(legend.isOverlay()); + legend.setOverlay(true); + assertTrue(legend.isOverlay()); + legend.setPosition(LegendPosition.TOP_RIGHT); + assertEquals(LegendPosition.TOP_RIGHT, legend.getPosition()); - // Category Axis Data - CTAxDataSource cat = ser.getCat(); - CTStrData strData = cat.getStrRef().getStrCache(); + XDDFManualLayout layout = legend.getOrAddManualLayout(); + assertNotNull(layout.getTarget()); + assertNotNull(layout.getXMode()); + assertNotNull(layout.getYMode()); + assertNotNull(layout.getHeightMode()); + assertNotNull(layout.getWidthMode()); + /* + * According to interface, 0.0 should be returned for + * uninitialized double properties. + */ + assertTrue(layout.getX() == 0.0); + assertTrue(layout.getY() == 0.0); + assertTrue(layout.getWidthRatio() == 0.0); + assertTrue(layout.getHeightRatio() == 0.0); - // Values - CTNumDataSource valSrc = ser.getVal(); - CTNumData numData = valSrc.getNumRef().getNumCache(); + final double newRatio = 1.1; + final double newCoordinate = 0.3; + final LayoutMode nonDefaultMode = LayoutMode.FACTOR; + final LayoutTarget nonDefaultTarget = LayoutTarget.OUTER; - strData.setPtArray(null); // unset old axis text - numData.setPtArray(null); // unset old values + layout.setWidthRatio(newRatio); + assertTrue(layout.getWidthRatio() == newRatio); - Map pieModel = new LinkedHashMap<>(); - pieModel.put("First", 1.0); - pieModel.put("Second", 3.0); - pieModel.put("Third", 4.0); + layout.setHeightRatio(newRatio); + assertTrue(layout.getHeightRatio() == newRatio); - // set model - int idx = 0; - int rownum = 1; - for(String key : pieModel.keySet()){ - double val = pieModel.get(key); + layout.setX(newCoordinate); + assertTrue(layout.getX() == newCoordinate); - CTNumVal numVal = numData.addNewPt(); - numVal.setIdx(idx); - numVal.setV("" + val); + layout.setY(newCoordinate); + assertTrue(layout.getY() == newCoordinate); - CTStrVal sVal = strData.addNewPt(); - sVal.setIdx(idx); - sVal.setV(key); + layout.setXMode(nonDefaultMode); + assertTrue(layout.getXMode() == nonDefaultMode); - idx++; - XSSFRow row = sheet.createRow(rownum++); - row.createCell(0).setCellValue(key); - row.createCell(1).setCellValue(val); - } - numData.getPtCount().setVal(idx); - strData.getPtCount().setVal(idx); - - String numDataRange = new CellRangeAddress(1, rownum-1, 1, 1).formatAsString(sheet.getSheetName(), true); - valSrc.getNumRef().setF(numDataRange); - String axisDataRange = new CellRangeAddress(1, rownum-1, 0, 0).formatAsString(sheet.getSheetName(), true); - cat.getStrRef().setF(axisDataRange); - - // updated the embedded workbook with the data - OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream(); - wb.write(xlsOut); - xlsOut.close(); - wb.close(); - } + layout.setYMode(nonDefaultMode); + assertTrue(layout.getYMode() == nonDefaultMode); + + layout.setWidthMode(nonDefaultMode); + assertTrue(layout.getWidthMode() == nonDefaultMode); + + layout.setHeightMode(nonDefaultMode); + assertTrue(layout.getHeightMode() == nonDefaultMode); + + layout.setTarget(nonDefaultTarget); + assertTrue(layout.getTarget() == nonDefaultTarget); + } + + private void checkAxisOperations(XDDFValueAxis axis) { + axis.setCrossBetween(AxisCrossBetween.MIDPOINT_CATEGORY); + assertEquals(AxisCrossBetween.MIDPOINT_CATEGORY, axis.getCrossBetween()); + + axis.setCrosses(AxisCrosses.AUTO_ZERO); + assertEquals(AxisCrosses.AUTO_ZERO, axis.getCrosses()); + + final String numberFormat = "General"; + axis.setNumberFormat(numberFormat); + assertEquals(numberFormat, axis.getNumberFormat()); + + axis.setPosition(AxisPosition.BOTTOM); + assertEquals(AxisPosition.BOTTOM, axis.getPosition()); + + axis.setMajorTickMark(AxisTickMark.NONE); + assertEquals(AxisTickMark.NONE, axis.getMajorTickMark()); + + axis.setMajorTickMark(AxisTickMark.IN); + assertEquals(AxisTickMark.IN, axis.getMajorTickMark()); + + axis.setMajorTickMark(AxisTickMark.OUT); + assertEquals(AxisTickMark.OUT, axis.getMajorTickMark()); + + axis.setMajorTickMark(AxisTickMark.CROSS); + assertEquals(AxisTickMark.CROSS, axis.getMajorTickMark()); + + axis.setMinorTickMark(AxisTickMark.NONE); + assertEquals(AxisTickMark.NONE, axis.getMinorTickMark()); + + axis.setMinorTickMark(AxisTickMark.IN); + assertEquals(AxisTickMark.IN, axis.getMinorTickMark()); + + axis.setMinorTickMark(AxisTickMark.OUT); + assertEquals(AxisTickMark.OUT, axis.getMinorTickMark()); + + axis.setMinorTickMark(AxisTickMark.CROSS); + assertEquals(AxisTickMark.CROSS, axis.getMinorTickMark()); + + axis.setVisible(true); + assertTrue(axis.isVisible()); + + axis.setVisible(false); + assertFalse(axis.isVisible()); + + final double EPSILON = 1E-7; + axis.setLogBase(Math.E); + assertTrue(Math.abs(axis.getLogBase() - Math.E) < EPSILON); + + final double newValue = 10.0; + + axis.setMinimum(newValue); + assertTrue(Math.abs(axis.getMinimum() - newValue) < EPSILON); + + axis.setMaximum(newValue); + assertTrue(Math.abs(axis.getMaximum() - newValue) < EPSILON); + + IllegalArgumentException iae = null; + try { + axis.setLogBase(0.0); + } catch (IllegalArgumentException e) { + iae = e; + } + assertNotNull(iae); -} \ No newline at end of file + iae = null; + try { + axis.setLogBase(30000.0); + } catch (IllegalArgumentException e) { + iae = e; + } + assertNotNull(iae); + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFChart.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFChart.java index f033e8ed4e..bdaacb8993 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFChart.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFChart.java @@ -17,46 +17,46 @@ package org.apache.poi.xssf.usermodel; -import junit.framework.TestCase; - import org.apache.poi.xssf.XSSFTestDataSamples; +import junit.framework.TestCase; + public final class TestXSSFChart extends TestCase { - + public void testGetAccessors() { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithThreeCharts.xlsx"); XSSFSheet s1 = wb.getSheetAt(0); XSSFSheet s2 = wb.getSheetAt(1); XSSFSheet s3 = wb.getSheetAt(2); - + assertEquals(0, s1.getRelations().size()); assertEquals(1, s2.getRelations().size()); assertEquals(1, s3.getRelations().size()); - + assertNotNull(XSSFTestDataSamples.writeOutAndReadBack(wb)); } - + public void testGetCharts() throws Exception { XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithThreeCharts.xlsx"); - + XSSFSheet s1 = wb.getSheetAt(0); XSSFSheet s2 = wb.getSheetAt(1); XSSFSheet s3 = wb.getSheetAt(2); - + assertEquals(0, s1.createDrawingPatriarch().getCharts().size()); assertEquals(2, s2.createDrawingPatriarch().getCharts().size()); assertEquals(1, s3.createDrawingPatriarch().getCharts().size()); - + // Check the titles XSSFChart chart = s2.createDrawingPatriarch().getCharts().get(0); assertEquals(null, chart.getTitleText()); - + chart = s2.createDrawingPatriarch().getCharts().get(1); assertEquals("Pie Chart Title Thingy", chart.getTitleText().getString()); - + chart = s3.createDrawingPatriarch().getCharts().get(0); assertEquals("Sheet 3 Chart with Title", chart.getTitleText().getString()); - + assertNotNull(XSSFTestDataSamples.writeOutAndReadBack(wb)); } @@ -68,14 +68,15 @@ public final class TestXSSFChart extends TestCase { XSSFChart c1 = d1.createChart(a1); assertEquals(1, d1.getCharts().size()); + assertNotNull(c1.getGraphicFrame()); - assertNotNull(c1.getOrCreateLegend()); + assertNotNull(c1.getOrAddLegend()); XSSFClientAnchor a2 = new XSSFClientAnchor(0, 0, 0, 0, 1, 11, 10, 60); XSSFChart c2 = d1.createChart(a2); assertNotNull(c2); assertEquals(2, d1.getCharts().size()); - + assertNotNull(XSSFTestDataSamples.writeOutAndReadBack(wb)); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFCategoryAxis.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFCategoryAxis.java index 64ce92b570..a7f9098e39 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFCategoryAxis.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFCategoryAxis.java @@ -17,24 +17,32 @@ package org.apache.poi.xssf.usermodel.charts; -import junit.framework.TestCase; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.apache.poi.ss.usermodel.charts.*; -import org.apache.poi.xssf.usermodel.*; +import junit.framework.TestCase; public final class TestXSSFCategoryAxis extends TestCase { - + public void testAccessMethods() throws Exception { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); XSSFChart chart = drawing.createChart(anchor); - XSSFCategoryAxis axis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); + XDDFCategoryAxis axis = chart.createCategoryAxis(AxisPosition.BOTTOM); axis.setCrosses(AxisCrosses.AUTO_ZERO); assertEquals(axis.getCrosses(), AxisCrosses.AUTO_ZERO); - assertEquals(chart.getAxis().size(), 1); + assertEquals(chart.getAxes().size(), 1); + + wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartAxis.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartAxis.java index 41e02b12ff..1578755ab1 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartAxis.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartAxis.java @@ -19,18 +19,22 @@ package org.apache.poi.xssf.usermodel.charts; import java.util.List; -import junit.framework.TestCase; - -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.*; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.AxisTickMark; +import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis; import org.apache.poi.xssf.XSSFTestDataSamples; -import org.apache.poi.xssf.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import junit.framework.TestCase; public final class TestXSSFChartAxis extends TestCase { private static final double EPSILON = 1E-7; - private final XSSFChartAxis axis; + private final XDDFChartAxis axis; public TestXSSFChartAxis() { super(); @@ -39,9 +43,9 @@ public final class TestXSSFChartAxis extends TestCase { XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); XSSFChart chart = drawing.createChart(anchor); - axis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); + axis = chart.createValueAxis(AxisPosition.BOTTOM); } - + public void testLogBaseIllegalArgument() throws Exception { IllegalArgumentException iae = null; try { @@ -119,13 +123,12 @@ public final class TestXSSFChartAxis extends TestCase { public void testGetChartAxisBug57362() { //Load existing excel with some chart on it having primary and secondary axis. - final Workbook workbook = XSSFTestDataSamples.openSampleWorkbook("57362.xlsx"); - final Sheet sh = workbook.getSheetAt(0); - final XSSFSheet xsh = (XSSFSheet) sh; - final XSSFDrawing drawing = xsh.createDrawingPatriarch(); + final XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook("57362.xlsx"); + final XSSFSheet sh = workbook.getSheetAt(0); + final XSSFDrawing drawing = sh.createDrawingPatriarch(); final XSSFChart chart = drawing.getCharts().get(0); - final List axisList = chart.getAxis(); + final List axisList = chart.getAxes(); assertEquals(4, axisList.size()); assertNotNull(axisList.get(0)); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartLegend.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartLegend.java index 524b421da9..2a464067c1 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartLegend.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartLegend.java @@ -23,13 +23,12 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.ChartLegend; -import org.apache.poi.ss.usermodel.charts.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; @@ -39,28 +38,28 @@ import org.junit.Test; public final class TestXSSFChartLegend { @Test public void testLegendPositionAccessMethods() throws IOException { - Workbook wb = new XSSFWorkbook(); - Sheet sheet = wb.createSheet(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); assertEquals(LegendPosition.TOP_RIGHT, legend.getPosition()); - + wb.close(); } @Test public void test_setOverlay_defaultChartLegend_expectOverlayInitialValueSetToFalse() throws IOException { // Arrange - Workbook wb = new XSSFWorkbook(); - Sheet sheet = wb.createSheet(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); // Act @@ -73,12 +72,12 @@ public final class TestXSSFChartLegend { @Test public void test_setOverlay_chartLegendSetToTrue_expectOverlayInitialValueSetToTrue() throws IOException { // Arrange - Workbook wb = new XSSFWorkbook(); - Sheet sheet = wb.createSheet(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); // Act legend.setOverlay(true); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java index 7282aa0f41..14f467a37f 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java @@ -26,24 +26,22 @@ import java.io.IOException; import java.util.List; import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.AxisCrosses; -import org.apache.poi.ss.usermodel.charts.AxisPosition; -import org.apache.poi.ss.usermodel.charts.ChartAxis; -import org.apache.poi.ss.usermodel.charts.ChartDataSource; -import org.apache.poi.ss.usermodel.charts.ChartLegend; -import org.apache.poi.ss.usermodel.charts.DataSources; -import org.apache.poi.ss.usermodel.charts.LegendPosition; -import org.apache.poi.ss.usermodel.charts.LineChartData; -import org.apache.poi.ss.usermodel.charts.ValueAxis; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.apache.poi.xssf.usermodel.XSSFSheet; @@ -54,9 +52,9 @@ import org.junit.Test; * Test get/set chart title. */ public class TestXSSFChartTitle { - private Workbook createWorkbookWithChart() { - Workbook wb = new XSSFWorkbook(); - Sheet sheet = wb.createSheet("linechart"); + private XSSFWorkbook createWorkbookWithChart() { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.createSheet("linechart"); final int NUM_OF_ROWS = 3; final int NUM_OF_COLUMNS = 10; @@ -71,28 +69,26 @@ public class TestXSSFChartTitle { } } - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); - LineChartData data = chart.getChartDataFactory().createLineChartData(); - // Use a category axis for the bottom axis. - ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); - ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); - ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); - ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); + XDDFDataSource xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); + XDDFNumericalDataSource ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); + XDDFChartData data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis); data.addSeries(xs, ys1); data.addSeries(xs, ys2); - - chart.plot(data, bottomAxis, leftAxis); + chart.plot(data); return wb; } @@ -100,16 +96,14 @@ public class TestXSSFChartTitle { /** * Gets the first chart from the named sheet in the workbook. */ - private XSSFChart getChartFromWorkbook(Workbook wb, String sheetName) { - Sheet sheet = wb.getSheet(sheetName); - if (sheet instanceof XSSFSheet) { - XSSFSheet xsheet = (XSSFSheet) sheet; - XSSFDrawing drawing = xsheet.getDrawingPatriarch(); - if (drawing != null) { - List charts = drawing.getCharts(); - if (charts != null && charts.size() > 0) { - return charts.get(0); - } + private XSSFChart getChartFromWorkbook(XSSFWorkbook wb, String sheetName) { + XSSFSheet sheet = wb.getSheet(sheetName); + XSSFSheet xsheet = sheet; + XSSFDrawing drawing = xsheet.getDrawingPatriarch(); + if (drawing != null) { + List charts = drawing.getCharts(); + if (charts != null && charts.size() > 0) { + return charts.get(0); } } return null; @@ -117,7 +111,7 @@ public class TestXSSFChartTitle { @Test public void testNewChart() throws IOException { - Workbook wb = createWorkbookWithChart(); + XSSFWorkbook wb = createWorkbookWithChart(); XSSFChart chart = getChartFromWorkbook(wb, "linechart"); assertNotNull(chart); assertNull(chart.getTitleText()); @@ -126,7 +120,7 @@ public class TestXSSFChartTitle { XSSFRichTextString queryTitle = chart.getTitleText(); assertNotNull(queryTitle); assertEquals(myTitle, queryTitle.toString()); - + final String myTitleFormula = "1 & \" and \" & 2"; chart.setTitleFormula(myTitleFormula); // setting formula should unset text, but since there is a formula, returns an empty string @@ -139,7 +133,7 @@ public class TestXSSFChartTitle { @Test public void testExistingChartWithTitle() throws IOException { - Workbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx"); + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx"); XSSFChart chart = getChartFromWorkbook(wb, "Sheet1"); assertNotNull(chart); XSSFRichTextString originalTitle = chart.getTitleText(); @@ -155,7 +149,7 @@ public class TestXSSFChartTitle { @Test public void testExistingChartNoTitle() throws IOException { - Workbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_noTitle.xlsx"); + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_noTitle.xlsx"); XSSFChart chart = getChartFromWorkbook(wb, "Sheet1"); assertNotNull(chart); assertNull(chart.getTitleText()); @@ -169,7 +163,7 @@ public class TestXSSFChartTitle { @Test public void testExistingChartWithFormulaTitle() throws IOException { - Workbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitleFormula.xlsx"); + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitleFormula.xlsx"); XSSFChart chart = getChartFromWorkbook(wb, "Sheet1"); assertNotNull(chart); XSSFRichTextString originalTitle = chart.getTitleText(); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFDateAxis.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFDateAxis.java index 81a474f502..5664488185 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFDateAxis.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFDateAxis.java @@ -17,24 +17,32 @@ package org.apache.poi.xssf.usermodel.charts; -import junit.framework.TestCase; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFDateAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.apache.poi.ss.usermodel.charts.*; -import org.apache.poi.xssf.usermodel.*; +import junit.framework.TestCase; public final class TestXSSFDateAxis extends TestCase { - + public void testAccessMethods() throws Exception { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); XSSFChart chart = drawing.createChart(anchor); - XSSFDateAxis axis = chart.getChartAxisFactory().createDateAxis(AxisPosition.BOTTOM); + XDDFDateAxis axis = chart.createDateAxis(AxisPosition.BOTTOM); axis.setCrosses(AxisCrosses.AUTO_ZERO); assertEquals(axis.getCrosses(), AxisCrosses.AUTO_ZERO); - assertEquals(chart.getAxis().size(), 1); + assertEquals(chart.getAxes().size(), 1); + + wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFLineChartData.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFLineChartData.java index e5ee56b504..83a25dff64 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFLineChartData.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFLineChartData.java @@ -22,19 +22,20 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.AxisPosition; -import org.apache.poi.ss.usermodel.charts.ChartAxis; -import org.apache.poi.ss.usermodel.charts.ChartDataSource; -import org.apache.poi.ss.usermodel.charts.DataSources; -import org.apache.poi.ss.usermodel.charts.LineChartData; -import org.apache.poi.ss.usermodel.charts.LineChartSeries; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.SheetBuilder; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; @@ -50,27 +51,26 @@ public class TestXSSFLineChartData { @Test public void testOneSeriePlot() throws IOException { - Workbook wb = new XSSFWorkbook(); - Sheet sheet = new SheetBuilder(wb, plotData).build(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, plotData).build(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); - ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); - ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); - LineChartData lineChartData = - chart.getChartDataFactory().createLineChartData(); + XDDFDataSource xs = XDDFDataSourcesFactory.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1")); + XDDFNumericalDataSource ys = XDDFDataSourcesFactory.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2")); - ChartDataSource xs = DataSources.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1")); - ChartDataSource ys = DataSources.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2")); - LineChartSeries series = lineChartData.addSeries(xs, ys); + XDDFChartData lineChartData = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis); + XDDFChartData.Series series = lineChartData.addSeries(xs, ys); assertNotNull(series); assertEquals(1, lineChartData.getSeries().size()); assertTrue(lineChartData.getSeries().contains(series)); - chart.plot(lineChartData, bottomAxis, leftAxis); + chart.plot(lineChartData); wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFManualLayout.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFManualLayout.java index 73df559144..85ac30f30f 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFManualLayout.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFManualLayout.java @@ -22,15 +22,14 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.ChartLegend; -import org.apache.poi.ss.usermodel.charts.LayoutMode; -import org.apache.poi.ss.usermodel.charts.LayoutTarget; -import org.apache.poi.ss.usermodel.charts.ManualLayout; +import org.apache.poi.xddf.usermodel.chart.LayoutMode; +import org.apache.poi.xddf.usermodel.chart.LayoutTarget; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFManualLayout; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.After; import org.junit.Before; @@ -38,25 +37,25 @@ import org.junit.Test; public final class TestXSSFManualLayout { - private Workbook wb; - private ManualLayout layout; - + private XSSFWorkbook wb; + private XDDFManualLayout layout; + @Before public void createEmptyLayout() { wb = new XSSFWorkbook(); - Sheet sheet = wb.createSheet(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); - ChartLegend legend = chart.getOrCreateLegend(); - layout = legend.getManualLayout(); + XSSFSheet sheet = wb.createSheet(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); + XDDFChartLegend legend = chart.getOrAddLegend(); + layout = legend.getOrAddManualLayout(); } @After public void closeWB() throws IOException { wb.close(); } - + /* * Accessor methods are not trivial. They use lazy underlying bean * initialization so there can be some errors (NPE, for example). diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFScatterChartData.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFScatterChartData.java index 79dc71c8d2..5683bc4052 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFScatterChartData.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFScatterChartData.java @@ -23,19 +23,21 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; -import org.apache.poi.ss.usermodel.Chart; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.charts.AxisPosition; -import org.apache.poi.ss.usermodel.charts.ChartAxis; -import org.apache.poi.ss.usermodel.charts.ChartDataSource; -import org.apache.poi.ss.usermodel.charts.DataSources; -import org.apache.poi.ss.usermodel.charts.ScatterChartData; -import org.apache.poi.ss.usermodel.charts.ScatterChartSeries; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.SheetBuilder; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.ScatterStyle; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFScatterChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.Test; @@ -51,27 +53,27 @@ public final class TestXSSFScatterChartData { @Test public void testOneSeriePlot() throws IOException { - Workbook wb = new XSSFWorkbook(); - Sheet sheet = new SheetBuilder(wb, plotData).build(); - Drawing drawing = sheet.createDrawingPatriarch(); - ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); - Chart chart = drawing.createChart(anchor); + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet) new SheetBuilder(wb, plotData).build(); + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); + XSSFChart chart = drawing.createChart(anchor); - ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); - ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + XDDFValueAxis bottomAxis = chart.createValueAxis(AxisPosition.BOTTOM); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); - ScatterChartData scatterChartData = - chart.getChartDataFactory().createScatterChartData(); + XDDFDataSource xs = XDDFDataSourcesFactory.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1")); + XDDFNumericalDataSource ys = XDDFDataSourcesFactory.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2")); - ChartDataSource xs = DataSources.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1")); - ChartDataSource ys = DataSources.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2")); - ScatterChartSeries series = scatterChartData.addSerie(xs, ys); + XDDFScatterChartData scatterChartData = (XDDFScatterChartData) chart.createData(ChartTypes.SCATTER, bottomAxis, leftAxis); + XDDFChartData.Series series = scatterChartData.addSeries(xs, ys); + assertEquals(ScatterStyle.LINE_MARKER, scatterChartData.getStyle()); assertNotNull(series); assertEquals(1, scatterChartData.getSeries().size()); assertTrue(scatterChartData.getSeries().contains(series)); - chart.plot(scatterChartData, bottomAxis, leftAxis); + chart.plot(scatterChartData); wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFValueAxis.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFValueAxis.java index db8ded3707..3f8ef06db4 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFValueAxis.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFValueAxis.java @@ -17,20 +17,27 @@ package org.apache.poi.xssf.usermodel.charts; -import junit.framework.TestCase; +import org.apache.poi.xddf.usermodel.chart.AxisCrossBetween; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xssf.usermodel.XSSFChart; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.apache.poi.ss.usermodel.charts.*; -import org.apache.poi.xssf.usermodel.*; +import junit.framework.TestCase; public final class TestXSSFValueAxis extends TestCase { - + public void testAccessMethods() throws Exception { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); XSSFChart chart = drawing.createChart(anchor); - XSSFValueAxis axis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); + XDDFValueAxis axis = chart.createValueAxis(AxisPosition.BOTTOM); axis.setCrossBetween(AxisCrossBetween.MIDPOINT_CATEGORY); assertEquals(axis.getCrossBetween(), AxisCrossBetween.MIDPOINT_CATEGORY); @@ -38,6 +45,8 @@ public final class TestXSSFValueAxis extends TestCase { axis.setCrosses(AxisCrosses.AUTO_ZERO); assertEquals(axis.getCrosses(), AxisCrosses.AUTO_ZERO); - assertEquals(chart.getAxis().size(), 1); + assertEquals(chart.getAxes().size(), 1); + + wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java index 1cb233dade..cb55371160 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFChart.java @@ -20,6 +20,8 @@ package org.apache.poi.xwpf.usermodel; import java.io.IOException; import java.util.List; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xwpf.XWPFTestDataSamples; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; @@ -33,7 +35,7 @@ import junit.framework.TestCase; public class TestXWPFChart extends TestCase { /** - * test method to check charts are null + * test method to check charts are not null */ public void testRead() throws IOException { @@ -41,10 +43,18 @@ public class TestXWPFChart extends TestCase { List charts = sampleDoc.getCharts(); assertNotNull(charts); assertEquals(2, charts.size()); - assertNotNull(charts.get(0)); - assertNotNull(charts.get(1)); + checkData(charts.get(0)); + checkData(charts.get(1)); } - + + private void checkData(XWPFChart chart) { + assertNotNull(chart); + assertEquals(1, chart.getChartSeries().size()); + XDDFChartData data = chart.getChartSeries().get(0); + assertEquals(XDDFBarChartData.class, data.getClass()); + assertEquals(3, data.getSeries().size()); + } + /** * test method to add chart title and check whether it's set */ diff --git a/test-data/slideshow/bar-chart.pptx b/test-data/slideshow/bar-chart.pptx new file mode 100644 index 0000000000000000000000000000000000000000..e4d2613046ab69e2d0a5c529b41cbcaa49ac4e30 GIT binary patch literal 44410 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 literal 0 HcmV?d00001 -- 2.39.5