You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XDDFChart.java 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. * ====================================================================
  18. */
  19. package org.apache.poi.xddf.usermodel.chart;
  20. import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
  21. import java.io.IOException;
  22. import java.io.OutputStream;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.HashMap;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Optional;
  30. import java.util.function.Function;
  31. import java.util.function.Predicate;
  32. import javax.xml.namespace.QName;
  33. import org.apache.poi.ooxml.POIXMLDocument;
  34. import org.apache.poi.ooxml.POIXMLDocumentPart;
  35. import org.apache.poi.ooxml.POIXMLException;
  36. import org.apache.poi.ooxml.POIXMLFactory;
  37. import org.apache.poi.ooxml.POIXMLRelation;
  38. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  39. import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
  40. import org.apache.poi.openxml4j.opc.PackagePart;
  41. import org.apache.poi.openxml4j.opc.PackageRelationship;
  42. import org.apache.poi.ss.util.CellRangeAddress;
  43. import org.apache.poi.ss.util.CellReference;
  44. import org.apache.poi.util.Beta;
  45. import org.apache.poi.util.Internal;
  46. import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
  47. import org.apache.poi.xddf.usermodel.text.TextContainer;
  48. import org.apache.poi.xddf.usermodel.text.XDDFTextBody;
  49. import org.apache.poi.xssf.usermodel.XSSFCell;
  50. import org.apache.poi.xssf.usermodel.XSSFRow;
  51. import org.apache.poi.xssf.usermodel.XSSFSheet;
  52. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  53. import org.apache.xmlbeans.XmlException;
  54. import org.apache.xmlbeans.XmlOptions;
  55. import org.openxmlformats.schemas.drawingml.x2006.chart.CTArea3DChart;
  56. import org.openxmlformats.schemas.drawingml.x2006.chart.CTAreaChart;
  57. import org.openxmlformats.schemas.drawingml.x2006.chart.CTBar3DChart;
  58. import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarChart;
  59. import org.openxmlformats.schemas.drawingml.x2006.chart.CTBoolean;
  60. import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx;
  61. import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
  62. import org.openxmlformats.schemas.drawingml.x2006.chart.CTChartSpace;
  63. import org.openxmlformats.schemas.drawingml.x2006.chart.CTDateAx;
  64. import org.openxmlformats.schemas.drawingml.x2006.chart.CTDoughnutChart;
  65. import org.openxmlformats.schemas.drawingml.x2006.chart.CTLine3DChart;
  66. import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineChart;
  67. import org.openxmlformats.schemas.drawingml.x2006.chart.CTPie3DChart;
  68. import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
  69. import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
  70. import org.openxmlformats.schemas.drawingml.x2006.chart.CTRadarChart;
  71. import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterChart;
  72. import org.openxmlformats.schemas.drawingml.x2006.chart.CTSerAx;
  73. import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurface;
  74. import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurface3DChart;
  75. import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurfaceChart;
  76. import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle;
  77. import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx;
  78. import org.openxmlformats.schemas.drawingml.x2006.chart.CTView3D;
  79. import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument;
  80. import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
  81. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
  82. import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
  83. @Beta
  84. public abstract class XDDFChart extends POIXMLDocumentPart implements TextContainer {
  85. /**
  86. * default width of chart in emu
  87. */
  88. public static final int DEFAULT_WIDTH = 500000;
  89. /**
  90. * default height of chart in emu
  91. */
  92. public static final int DEFAULT_HEIGHT = 500000;
  93. /**
  94. * default x-coordinate of chart in emu
  95. */
  96. public static final int DEFAULT_X = 10;
  97. /**
  98. * default y-coordinate value of chart in emu
  99. */
  100. public static final int DEFAULT_Y = 10;
  101. /**
  102. * Underlying workbook
  103. */
  104. private XSSFWorkbook workbook;
  105. private int chartIndex = 0;
  106. protected List<XDDFChartAxis> axes = new ArrayList<>();
  107. /**
  108. * Root element of the Chart part
  109. */
  110. protected final CTChartSpace chartSpace;
  111. /**
  112. * Chart element in the chart space
  113. */
  114. protected final CTChart chart;
  115. /**
  116. * Construct a chart.
  117. */
  118. protected XDDFChart() {
  119. super();
  120. chartSpace = CTChartSpace.Factory.newInstance();
  121. chart = chartSpace.addNewChart();
  122. chart.addNewPlotArea();
  123. }
  124. /**
  125. * Construct a DrawingML chart from a package part.
  126. *
  127. * @param part
  128. * the package part holding the chart data, the content type must
  129. * be
  130. * <code>application/vnd.openxmlformats-officedocument.drawingml.chart+xml</code>
  131. * @since POI 3.14-Beta1
  132. */
  133. protected XDDFChart(PackagePart part) throws IOException, XmlException {
  134. super(part);
  135. chartSpace = ChartSpaceDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getChartSpace();
  136. chart = chartSpace.getChart();
  137. }
  138. /**
  139. * Return the underlying CTChartSpace bean, the root element of the Chart
  140. * part.
  141. *
  142. * @return the underlying CTChartSpace bean
  143. */
  144. @Internal
  145. public CTChartSpace getCTChartSpace() {
  146. return chartSpace;
  147. }
  148. /**
  149. * Return the underlying CTChart bean, within the Chart Space
  150. *
  151. * @return the underlying CTChart bean
  152. */
  153. @Internal
  154. public CTChart getCTChart() {
  155. return chart;
  156. }
  157. /**
  158. * Return the underlying CTPlotArea bean, within the Chart
  159. *
  160. * @return the underlying CTPlotArea bean
  161. */
  162. @Internal
  163. protected CTPlotArea getCTPlotArea() {
  164. return chart.getPlotArea();
  165. }
  166. /**
  167. * Clear all properties, as if a new instance had just been created.
  168. * @since POI 4.1.2
  169. */
  170. public void clear() {
  171. axes.clear();
  172. seriesCount = 0;
  173. if (workbook != null) {
  174. workbook.removeSheetAt(0);
  175. workbook.createSheet();
  176. }
  177. chart.set(CTChart.Factory.newInstance());
  178. chart.addNewPlotArea();
  179. }
  180. /**
  181. * @return true if only visible cells will be present on the chart, false
  182. * otherwise
  183. */
  184. public boolean isPlotOnlyVisibleCells() {
  185. if (chart.isSetPlotVisOnly()) {
  186. return chart.getPlotVisOnly().getVal();
  187. } else {
  188. return false;
  189. }
  190. }
  191. /**
  192. * @param only
  193. * a flag specifying if only visible cells should be present on
  194. * the chart
  195. */
  196. public void setPlotOnlyVisibleCells(boolean only) {
  197. if (!chart.isSetPlotVisOnly()) {
  198. chart.setPlotVisOnly(CTBoolean.Factory.newInstance());
  199. }
  200. chart.getPlotVisOnly().setVal(only);
  201. }
  202. public void setFloor(int thickness) {
  203. if (!chart.isSetFloor()) {
  204. chart.setFloor(CTSurface.Factory.newInstance());
  205. }
  206. chart.getFloor().getThickness().setVal(thickness);
  207. }
  208. public void setBackWall(int thickness) {
  209. if (!chart.isSetBackWall()) {
  210. chart.setBackWall(CTSurface.Factory.newInstance());
  211. }
  212. chart.getBackWall().getThickness().setVal(thickness);
  213. }
  214. public void setSideWall(int thickness) {
  215. if (!chart.isSetSideWall()) {
  216. chart.setSideWall(CTSurface.Factory.newInstance());
  217. }
  218. chart.getSideWall().getThickness().setVal(thickness);
  219. }
  220. public void setAutoTitleDeleted(boolean deleted) {
  221. if (!chart.isSetAutoTitleDeleted()) {
  222. chart.setAutoTitleDeleted(CTBoolean.Factory.newInstance());
  223. }
  224. chart.getAutoTitleDeleted().setVal(deleted);
  225. if (deleted && chart.isSetTitle()) {
  226. chart.unsetTitle();
  227. }
  228. }
  229. /**
  230. * @since 4.0.1
  231. *
  232. */
  233. public void displayBlanksAs(DisplayBlanks as) {
  234. if (as == null){
  235. if (chart.isSetDispBlanksAs()) {
  236. chart.unsetDispBlanksAs();
  237. }
  238. } else {
  239. if (chart.isSetDispBlanksAs()) {
  240. chart.getDispBlanksAs().setVal(as.underlying);
  241. } else {
  242. chart.addNewDispBlanksAs().setVal(as.underlying);
  243. }
  244. }
  245. }
  246. /**
  247. * @since 4.0.1
  248. */
  249. public Boolean getTitleOverlay() {
  250. if (chart.isSetTitle()) {
  251. CTTitle title = chart.getTitle();
  252. if (title.isSetOverlay()) {
  253. return title.getOverlay().getVal();
  254. }
  255. }
  256. return null;
  257. }
  258. /**
  259. * @since 4.0.1
  260. */
  261. public void setTitleOverlay(boolean overlay) {
  262. if (!chart.isSetTitle()) {
  263. chart.addNewTitle();
  264. }
  265. new XDDFTitle(this, chart.getTitle()).setOverlay(overlay);
  266. }
  267. /**
  268. * Sets the title text as a static string.
  269. *
  270. * @param text
  271. * to use as new title
  272. * @since 4.0.1
  273. */
  274. public void setTitleText(String text) {
  275. if (!chart.isSetTitle()) {
  276. chart.addNewTitle();
  277. }
  278. new XDDFTitle(this, chart.getTitle()).setText(text);
  279. }
  280. /**
  281. * @since 4.0.1
  282. */
  283. public XDDFTitle getTitle() {
  284. if (chart.isSetTitle()) {
  285. return new XDDFTitle(this, chart.getTitle());
  286. } else {
  287. return null;
  288. }
  289. }
  290. /**
  291. * Remove the chart title.
  292. * @since POI 5.0.0
  293. */
  294. public void removeTitle() {
  295. setAutoTitleDeleted(true);
  296. }
  297. /**
  298. * Get or Add chart 3D view into chart
  299. *
  300. * @return this method will add 3D view
  301. */
  302. public XDDFView3D getOrAddView3D() {
  303. CTView3D view3D;
  304. if (chart.isSetView3D()) {
  305. view3D = chart.getView3D();
  306. } else {
  307. view3D = chart.addNewView3D();
  308. }
  309. return new XDDFView3D(view3D);
  310. }
  311. /**
  312. * Get the chart title body if there is one, i.e. title is set and is not a
  313. * formula.
  314. *
  315. * @return text body or null, if title is a formula or no title is set.
  316. */
  317. @Beta
  318. public XDDFTextBody getFormattedTitle() {
  319. if (!chart.isSetTitle()) {
  320. return null;
  321. }
  322. return new XDDFTitle(this, chart.getTitle()).getBody();
  323. }
  324. @Override
  325. public <R> Optional<R> findDefinedParagraphProperty(Predicate<CTTextParagraphProperties> isSet,
  326. Function<CTTextParagraphProperties, R> getter) {
  327. // TODO Auto-generated method stub
  328. return Optional.empty();
  329. }
  330. @Override
  331. public <R> Optional<R> findDefinedRunProperty(Predicate<CTTextCharacterProperties> isSet,
  332. Function<CTTextCharacterProperties, R> getter) {
  333. // TODO Auto-generated method stub
  334. return Optional.empty();
  335. }
  336. public XDDFShapeProperties getOrAddShapeProperties() {
  337. CTPlotArea plotArea = getCTPlotArea();
  338. CTShapeProperties properties;
  339. if (plotArea.isSetSpPr()) {
  340. properties = plotArea.getSpPr();
  341. } else {
  342. properties = plotArea.addNewSpPr();
  343. }
  344. return new XDDFShapeProperties(properties);
  345. }
  346. public void deleteShapeProperties() {
  347. if (getCTPlotArea().isSetSpPr()) {
  348. getCTPlotArea().unsetSpPr();
  349. }
  350. }
  351. public XDDFChartLegend getOrAddLegend() {
  352. return new XDDFChartLegend(chart);
  353. }
  354. public void deleteLegend() {
  355. if (chart.isSetLegend()) {
  356. chart.unsetLegend();
  357. }
  358. }
  359. public XDDFManualLayout getOrAddManualLayout() {
  360. return new XDDFManualLayout(getCTPlotArea());
  361. }
  362. private long seriesCount = 0;
  363. protected long incrementSeriesCount() {
  364. return seriesCount++;
  365. }
  366. public void plot(XDDFChartData data) {
  367. XSSFSheet sheet = getSheet();
  368. for (int idx = 0; idx < data.getSeriesCount(); idx++) {
  369. XDDFChartData.Series series = data.getSeries(idx);
  370. series.plot();
  371. XDDFDataSource<?> categoryDS = series.getCategoryData();
  372. XDDFNumericalDataSource<? extends Number> valuesDS = series.getValuesData();
  373. if (categoryDS.isCellRange() || valuesDS.isCellRange()
  374. || categoryDS.isLiteral() || valuesDS.isLiteral()) {
  375. // let's assume the data is already in the sheet
  376. } else {
  377. fillSheet(sheet, categoryDS, valuesDS);
  378. }
  379. }
  380. }
  381. public List<XDDFChartData> getChartSeries() {
  382. List<XDDFChartData> series = new LinkedList<>();
  383. CTPlotArea plotArea = getCTPlotArea();
  384. Map<Long, XDDFChartAxis> categories = getCategoryAxes();
  385. Map<Long, XDDFValueAxis> values = getValueAxes();
  386. for (int i = 0; i < plotArea.sizeOfAreaChartArray(); i++) {
  387. CTAreaChart areaChart = plotArea.getAreaChartArray(i);
  388. series.add(new XDDFAreaChartData(this, areaChart, categories, values));
  389. }
  390. for (int i = 0; i < plotArea.sizeOfArea3DChartArray(); i++) {
  391. CTArea3DChart areaChart = plotArea.getArea3DChartArray(i);
  392. series.add(new XDDFArea3DChartData(this, areaChart, categories, values));
  393. }
  394. for (int i = 0; i < plotArea.sizeOfBarChartArray(); i++) {
  395. CTBarChart barChart = plotArea.getBarChartArray(i);
  396. series.add(new XDDFBarChartData(this, barChart, categories, values));
  397. }
  398. for (int i = 0; i < plotArea.sizeOfBar3DChartArray(); i++) {
  399. CTBar3DChart barChart = plotArea.getBar3DChartArray(i);
  400. series.add(new XDDFBar3DChartData(this, barChart, categories, values));
  401. }
  402. for (int i = 0; i < plotArea.sizeOfDoughnutChartArray(); i++) {
  403. CTDoughnutChart doughnutChart = plotArea.getDoughnutChartArray(i);
  404. series.add(new XDDFDoughnutChartData(this, doughnutChart));
  405. }
  406. for (int i = 0; i < plotArea.sizeOfLineChartArray(); i++) {
  407. CTLineChart lineChart = plotArea.getLineChartArray(i);
  408. series.add(new XDDFLineChartData(this, lineChart, categories, values));
  409. }
  410. for (int i = 0; i < plotArea.sizeOfLine3DChartArray(); i++) {
  411. CTLine3DChart lineChart = plotArea.getLine3DChartArray(i);
  412. series.add(new XDDFLine3DChartData(this, lineChart, categories, values));
  413. }
  414. for (int i = 0; i < plotArea.sizeOfPieChartArray(); i++) {
  415. CTPieChart pieChart = plotArea.getPieChartArray(i);
  416. series.add(new XDDFPieChartData(this, pieChart));
  417. }
  418. for (int i = 0; i < plotArea.sizeOfPie3DChartArray(); i++) {
  419. CTPie3DChart pieChart = plotArea.getPie3DChartArray(i);
  420. series.add(new XDDFPie3DChartData(this, pieChart));
  421. }
  422. for (int i = 0; i < plotArea.sizeOfRadarChartArray(); i++) {
  423. CTRadarChart radarChart = plotArea.getRadarChartArray(i);
  424. series.add(new XDDFRadarChartData(this, radarChart, categories, values));
  425. }
  426. for (int i = 0; i < plotArea.sizeOfScatterChartArray(); i++) {
  427. CTScatterChart scatterChart = plotArea.getScatterChartArray(i);
  428. series.add(new XDDFScatterChartData(this, scatterChart, categories, values));
  429. }
  430. for (int i = 0; i < plotArea.sizeOfSurfaceChartArray(); i++) {
  431. CTSurfaceChart surfaceChart = plotArea.getSurfaceChartArray(i);
  432. series.add(new XDDFSurfaceChartData(this, surfaceChart, categories, values));
  433. }
  434. for (int i = 0; i < plotArea.sizeOfSurface3DChartArray(); i++) {
  435. CTSurface3DChart surfaceChart = plotArea.getSurface3DChartArray(i);
  436. series.add(new XDDFSurface3DChartData(this, surfaceChart, categories, values));
  437. }
  438. // TODO repeat above code for missing charts: Bubble, OfPie and Stock
  439. seriesCount = series.size();
  440. return series;
  441. }
  442. /**
  443. * Clear all chart series, as if a new instance had just been created.
  444. * @since POI 4.1.2
  445. */
  446. public void clearChartSeries() {
  447. CTPlotArea plotArea = getCTPlotArea();
  448. for (int i = plotArea.sizeOfAreaChartArray(); i > 0; i--) {
  449. plotArea.removeAreaChart(i - 1);
  450. }
  451. for (int i = plotArea.sizeOfArea3DChartArray(); i > 0; i--) {
  452. plotArea.removeArea3DChart(i - 1);
  453. }
  454. for (int i = plotArea.sizeOfBarChartArray(); i > 0; i--) {
  455. plotArea.removeBarChart(i - 1);
  456. }
  457. for (int i = plotArea.sizeOfBar3DChartArray(); i > 0; i--) {
  458. plotArea.removeBar3DChart(i - 1);
  459. }
  460. for (int i = plotArea.sizeOfBubbleChartArray(); i > 0; i--) {
  461. plotArea.removeBubbleChart(i - 1);
  462. }
  463. for (int i = plotArea.sizeOfDoughnutChartArray(); i > 0; i--) {
  464. plotArea.removeDoughnutChart(i - 1);
  465. }
  466. for (int i = plotArea.sizeOfLineChartArray(); i > 0; i--) {
  467. plotArea.removeLineChart(i - 1);
  468. }
  469. for (int i = plotArea.sizeOfLine3DChartArray(); i > 0; i--) {
  470. plotArea.removeLine3DChart(i - 1);
  471. }
  472. for (int i = plotArea.sizeOfOfPieChartArray(); i > 0; i--) {
  473. plotArea.removeOfPieChart(i - 1);
  474. }
  475. for (int i = plotArea.sizeOfPieChartArray(); i > 0; i--) {
  476. plotArea.removePieChart(i - 1);
  477. }
  478. for (int i = plotArea.sizeOfPie3DChartArray(); i > 0; i--) {
  479. plotArea.removePie3DChart(i - 1);
  480. }
  481. for (int i = plotArea.sizeOfRadarChartArray(); i > 0; i--) {
  482. plotArea.removeRadarChart(i - 1);
  483. }
  484. for (int i = plotArea.sizeOfScatterChartArray(); i > 0; i--) {
  485. plotArea.removeScatterChart(i - 1);
  486. }
  487. for (int i = plotArea.sizeOfStockChartArray(); i > 0; i--) {
  488. plotArea.removeStockChart(i - 1);
  489. }
  490. for (int i = plotArea.sizeOfSurfaceChartArray(); i > 0; i--) {
  491. plotArea.removeSurfaceChart(i - 1);
  492. }
  493. for (int i = plotArea.sizeOfSurface3DChartArray(); i > 0; i--) {
  494. plotArea.removeSurface3DChart(i - 1);
  495. }
  496. }
  497. private Map<Long, XDDFChartAxis> getCategoryAxes() {
  498. CTPlotArea plotArea = getCTPlotArea();
  499. int sizeOfArray = plotArea.sizeOfCatAxArray();
  500. Map<Long, XDDFChartAxis> axesMap = new HashMap<>(sizeOfArray);
  501. for (int i = 0; i < sizeOfArray; i++) {
  502. CTCatAx category = plotArea.getCatAxArray(i);
  503. axesMap.put(category.getAxId().getVal(), new XDDFCategoryAxis(category));
  504. }
  505. return axesMap;
  506. }
  507. private Map<Long, XDDFValueAxis> getValueAxes() {
  508. CTPlotArea plotArea = getCTPlotArea();
  509. int sizeOfArray = plotArea.sizeOfValAxArray();
  510. Map<Long, XDDFValueAxis> axesMap = new HashMap<>(sizeOfArray);
  511. for (int i = 0; i < sizeOfArray; i++) {
  512. CTValAx values = plotArea.getValAxArray(i);
  513. axesMap.put(values.getAxId().getVal(), new XDDFValueAxis(values));
  514. }
  515. return axesMap;
  516. }
  517. public XDDFValueAxis createValueAxis(AxisPosition pos) {
  518. XDDFValueAxis valueAxis = new XDDFValueAxis(getCTPlotArea(), pos);
  519. addAxis(valueAxis);
  520. return valueAxis;
  521. }
  522. /**
  523. * this method will return series axis with specified position
  524. *
  525. * @param pos axis position Left, Right, Top, Bottom
  526. * @return series axis with specified position
  527. */
  528. public XDDFSeriesAxis createSeriesAxis(AxisPosition pos) {
  529. XDDFSeriesAxis seriesAxis = new XDDFSeriesAxis(getCTPlotArea(), pos);
  530. addAxis(seriesAxis);
  531. return seriesAxis;
  532. }
  533. public XDDFCategoryAxis createCategoryAxis(AxisPosition pos) {
  534. XDDFCategoryAxis categoryAxis = new XDDFCategoryAxis(getCTPlotArea(), pos);
  535. addAxis(categoryAxis);
  536. return categoryAxis;
  537. }
  538. public XDDFDateAxis createDateAxis(AxisPosition pos) {
  539. XDDFDateAxis dateAxis = new XDDFDateAxis(getCTPlotArea(), pos);
  540. addAxis(dateAxis);
  541. return dateAxis;
  542. }
  543. private void addAxis(XDDFChartAxis newAxis) {
  544. if (axes.size() == 1) {
  545. XDDFChartAxis axis = axes.get(0);
  546. axis.crossAxis(newAxis);
  547. newAxis.crossAxis(axis);
  548. axis.setCrosses(AxisCrosses.AUTO_ZERO);
  549. newAxis.setCrosses(AxisCrosses.AUTO_ZERO);
  550. }
  551. axes.add(newAxis);
  552. }
  553. /**
  554. * this method will return specified chart data with category and series values
  555. *
  556. * @param type chart type
  557. * @param category category values of chart
  558. * @param values series values of chart
  559. * @return specified chart data.
  560. */
  561. public XDDFChartData createData(ChartTypes type, XDDFChartAxis category, XDDFValueAxis values) {
  562. Map<Long, XDDFChartAxis> categories = null;
  563. Map<Long, XDDFValueAxis> mapValues = null;
  564. if (ChartTypes.PIE != type && ChartTypes.PIE3D != type) {
  565. categories = Collections.singletonMap(category.getId(), category);
  566. mapValues = Collections.singletonMap(values.getId(), values);
  567. }
  568. final CTPlotArea plotArea = getCTPlotArea();
  569. switch (type) {
  570. case AREA:
  571. return new XDDFAreaChartData(this, plotArea.addNewAreaChart(), categories, mapValues);
  572. case AREA3D:
  573. return new XDDFArea3DChartData(this, plotArea.addNewArea3DChart(), categories, mapValues);
  574. case BAR:
  575. return new XDDFBarChartData(this, plotArea.addNewBarChart(), categories, mapValues);
  576. case BAR3D:
  577. return new XDDFBar3DChartData(this, plotArea.addNewBar3DChart(), categories, mapValues);
  578. case DOUGHNUT:
  579. return new XDDFDoughnutChartData(this, plotArea.addNewDoughnutChart());
  580. case LINE:
  581. return new XDDFLineChartData(this, plotArea.addNewLineChart(), categories, mapValues);
  582. case LINE3D:
  583. return new XDDFLine3DChartData(this, plotArea.addNewLine3DChart(), categories, mapValues);
  584. case PIE:
  585. return new XDDFPieChartData(this, plotArea.addNewPieChart());
  586. case PIE3D:
  587. return new XDDFPie3DChartData(this, plotArea.addNewPie3DChart());
  588. case RADAR:
  589. return new XDDFRadarChartData(this, plotArea.addNewRadarChart(), categories, mapValues);
  590. case SCATTER:
  591. return new XDDFScatterChartData(this, plotArea.addNewScatterChart(), categories, mapValues);
  592. case SURFACE:
  593. return new XDDFSurfaceChartData(this, plotArea.addNewSurfaceChart(), categories, mapValues);
  594. case SURFACE3D:
  595. return new XDDFSurface3DChartData(this, plotArea.addNewSurface3DChart(), categories, mapValues);
  596. // TODO repeat above code for missing charts: Bubble, OfPie and Stock
  597. default:
  598. return null;
  599. }
  600. }
  601. public List<? extends XDDFChartAxis> getAxes() {
  602. if (axes.isEmpty() && hasAxes()) {
  603. parseAxes();
  604. }
  605. return axes;
  606. }
  607. private boolean hasAxes() {
  608. CTPlotArea ctPlotArea = getCTPlotArea();
  609. int totalAxisCount = ctPlotArea.sizeOfValAxArray() + ctPlotArea.sizeOfCatAxArray() + ctPlotArea
  610. .sizeOfDateAxArray() + ctPlotArea.sizeOfSerAxArray();
  611. return totalAxisCount > 0;
  612. }
  613. private void parseAxes() {
  614. for (CTCatAx catAx : getCTPlotArea().getCatAxArray()) {
  615. axes.add(new XDDFCategoryAxis(catAx));
  616. }
  617. for (CTDateAx dateAx : getCTPlotArea().getDateAxArray()) {
  618. axes.add(new XDDFDateAxis(dateAx));
  619. }
  620. for (CTSerAx serAx : getCTPlotArea().getSerAxArray()) {
  621. axes.add(new XDDFSeriesAxis(serAx));
  622. }
  623. for (CTValAx valAx : getCTPlotArea().getValAxArray()) {
  624. axes.add(new XDDFValueAxis(valAx));
  625. }
  626. }
  627. /**
  628. * Set value range (basic Axis Options)
  629. *
  630. * @param axisIndex
  631. * 0 - primary axis, 1 - secondary axis
  632. * @param minimum
  633. * minimum value; Double.NaN - automatic; null - no change
  634. * @param maximum
  635. * maximum value; Double.NaN - automatic; null - no change
  636. * @param majorUnit
  637. * major unit value; Double.NaN - automatic; null - no change
  638. * @param minorUnit
  639. * minor unit value; Double.NaN - automatic; null - no change
  640. */
  641. public void setValueRange(int axisIndex, Double minimum, Double maximum, Double majorUnit, Double minorUnit) {
  642. XDDFChartAxis axis = getAxes().get(axisIndex);
  643. if (axis == null) {
  644. return;
  645. }
  646. if (minimum != null) {
  647. axis.setMinimum(minimum);
  648. }
  649. if (maximum != null) {
  650. axis.setMaximum(maximum);
  651. }
  652. if (majorUnit != null) {
  653. axis.setMajorUnit(majorUnit);
  654. }
  655. if (minorUnit != null) {
  656. axis.setMinorUnit(minorUnit);
  657. }
  658. }
  659. /**
  660. * method to create relationship with embedded part for example writing xlsx
  661. * file stream into output stream
  662. *
  663. * @param chartRelation
  664. * relationship object
  665. * @param chartFactory
  666. * ChartFactory object
  667. * @param chartIndex
  668. * index used to suffix on file
  669. * @return return relation part which used to write relation in .rels file
  670. * and get relation id
  671. * @since POI 4.0.0
  672. */
  673. public PackageRelationship createRelationshipInChart(POIXMLRelation chartRelation, POIXMLFactory chartFactory,
  674. int chartIndex) {
  675. POIXMLDocumentPart documentPart =
  676. createRelationship(chartRelation, chartFactory, chartIndex, true).getDocumentPart();
  677. return addRelation(null, chartRelation, documentPart).getRelationship();
  678. }
  679. /**
  680. * if embedded part was null then create new part
  681. *
  682. * @param chartWorkbookRelation
  683. * chart workbook relation object
  684. * @param chartFactory
  685. * factory object of POIXMLFactory (XWPFFactory/XSLFFactory)
  686. * @return return the new package part
  687. * @throws InvalidFormatException
  688. * @since POI 4.0.0
  689. */
  690. private PackagePart createWorksheetPart(POIXMLRelation chartWorkbookRelation, POIXMLFactory chartFactory)
  691. throws InvalidFormatException {
  692. PackageRelationship xlsx = createRelationshipInChart(chartWorkbookRelation, chartFactory, chartIndex);
  693. setExternalId(xlsx.getId());
  694. return getTargetPart(xlsx);
  695. }
  696. /**
  697. * this method write the XSSFWorkbook object data into embedded excel file
  698. *
  699. * @param workbook
  700. * XSSFworkbook object
  701. * @throws IOException
  702. * @throws InvalidFormatException
  703. * @since POI 4.0.0
  704. */
  705. public void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException {
  706. PackagePart worksheetPart = getWorksheetPart();
  707. if (worksheetPart == null) {
  708. POIXMLRelation chartWorkbookRelation = getChartWorkbookRelation();
  709. POIXMLFactory chartFactory = getChartFactory();
  710. if (chartWorkbookRelation != null && chartFactory != null) {
  711. worksheetPart = createWorksheetPart(chartWorkbookRelation, chartFactory);
  712. } else {
  713. throw new InvalidFormatException("unable to determine chart relations");
  714. }
  715. }
  716. try (OutputStream xlsOut = worksheetPart.getOutputStream()) {
  717. setWorksheetPartCommitted();
  718. workbook.write(xlsOut);
  719. }
  720. }
  721. /**
  722. *
  723. * @return the chart relation in the implementing subclass.
  724. * @since POI 4.0.0
  725. */
  726. protected abstract POIXMLRelation getChartRelation();
  727. /**
  728. *
  729. * @return the chart workbook relation in the implementing subclass.
  730. * @since POI 4.0.0
  731. */
  732. protected abstract POIXMLRelation getChartWorkbookRelation();
  733. /**
  734. *
  735. * @return the chart factory in the implementing subclass.
  736. * @since POI 4.0.0
  737. */
  738. protected abstract POIXMLFactory getChartFactory();
  739. /**
  740. * this method writes the data into sheet
  741. *
  742. * @param sheet
  743. * sheet of embedded excel
  744. * @param categoryData
  745. * category values
  746. * @param valuesData
  747. * data values
  748. * @since POI 4.0.0
  749. */
  750. protected void fillSheet(XSSFSheet sheet, XDDFDataSource<?> categoryData, XDDFNumericalDataSource<?> valuesData) {
  751. int numOfPoints = categoryData.getPointCount();
  752. for (int i = 0; i < numOfPoints; i++) {
  753. XSSFRow row = getRow(sheet, i + 1); // first row is for title
  754. Object category = categoryData.getPointAt(i);
  755. if (category != null) {
  756. getCell(row, categoryData.getColIndex()).setCellValue(category.toString());
  757. }
  758. Number value = valuesData.getPointAt(i);
  759. if (value != null) {
  760. getCell(row, valuesData.getColIndex()).setCellValue(value.doubleValue());
  761. }
  762. }
  763. }
  764. /**
  765. * this method return row on given index if row is null then create new row
  766. *
  767. * @param sheet
  768. * current sheet object
  769. * @param index
  770. * index of current row
  771. * @return this method return sheet row on given index
  772. * @since POI 4.0.0
  773. */
  774. private XSSFRow getRow(XSSFSheet sheet, int index) {
  775. XSSFRow row = sheet.getRow(index);
  776. if (row == null) {
  777. return sheet.createRow(index);
  778. } else {
  779. return row;
  780. }
  781. }
  782. /**
  783. * this method return cell on given index if cell is null then create new
  784. * cell
  785. *
  786. * @param row
  787. * current row object
  788. * @param index
  789. * index of current cell
  790. * @return this method return sheet cell on given index
  791. * @since POI 4.0.0
  792. */
  793. private XSSFCell getCell(XSSFRow row, int index) {
  794. XSSFCell cell = row.getCell(index);
  795. if (cell == null) {
  796. return row.createCell(index);
  797. } else {
  798. return cell;
  799. }
  800. }
  801. /**
  802. * import content from other chart to created chart
  803. *
  804. * @param other
  805. * chart object
  806. * @since POI 4.0.0
  807. */
  808. public void importContent(XDDFChart other) {
  809. this.chart.set(other.chart);
  810. }
  811. /**
  812. * save chart xml
  813. */
  814. @Override
  815. protected void commit() throws IOException {
  816. XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
  817. xmlOptions.setSaveSyntheticDocumentElement(
  818. new QName(CTChartSpace.type.getName().getNamespaceURI(), "chartSpace", "c"));
  819. if (workbook != null) {
  820. try {
  821. saveWorkbook(workbook);
  822. } catch (InvalidFormatException e) {
  823. throw new POIXMLException(e);
  824. }
  825. }
  826. PackagePart part = getPackagePart();
  827. try (OutputStream out = part.getOutputStream()) {
  828. chartSpace.save(out, xmlOptions);
  829. }
  830. }
  831. /**
  832. * set sheet title in excel file
  833. *
  834. * @param title
  835. * title of sheet
  836. * @param column
  837. * column index
  838. * @return return cell reference
  839. * @since POI 4.0.0
  840. */
  841. public CellReference setSheetTitle(String title, int column) {
  842. XSSFSheet sheet = getSheet();
  843. if (sheet == null) {
  844. return null;
  845. }
  846. XSSFRow row = getRow(sheet, 0);
  847. XSSFCell cell = getCell(row, column);
  848. cell.setCellValue(title);
  849. return new CellReference(sheet.getSheetName(), 0, column, true, true);
  850. }
  851. /**
  852. * @param range
  853. * @return
  854. * @since POI 4.0.0
  855. */
  856. public String formatRange(CellRangeAddress range) {
  857. final XSSFSheet sheet = getSheet();
  858. return (sheet == null) ? null : range.formatAsString(sheet.getSheetName(), true);
  859. }
  860. /**
  861. * get sheet object of embedded excel file
  862. *
  863. * @return excel sheet object
  864. * @since POI 4.0.0
  865. */
  866. private XSSFSheet getSheet() {
  867. XSSFSheet sheet = null;
  868. try {
  869. sheet = getWorkbook().getSheetAt(0);
  870. } catch (InvalidFormatException | IOException ife) {
  871. }
  872. return sheet;
  873. }
  874. /**
  875. * this method is used to get worksheet part if call is from saveworkbook
  876. * method then check isCommitted isCommitted variable shows that we are
  877. * writing xssfworkbook object into output stream of embedded part
  878. *
  879. * @return returns the packagepart of embedded file
  880. * @throws InvalidFormatException
  881. * @since POI 4.0.0
  882. */
  883. private PackagePart getWorksheetPart() throws InvalidFormatException {
  884. for (RelationPart part : getRelationParts()) {
  885. if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) {
  886. return getTargetPart(part.getRelationship());
  887. }
  888. }
  889. return null;
  890. }
  891. private void setWorksheetPartCommitted() {
  892. for (RelationPart part : getRelationParts()) {
  893. if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) {
  894. part.getDocumentPart().setCommitted(true);
  895. break;
  896. }
  897. }
  898. }
  899. /**
  900. * @return returns the workbook object of embedded excel file
  901. * @throws IOException
  902. * @throws InvalidFormatException
  903. * @since POI 4.0.0
  904. */
  905. public XSSFWorkbook getWorkbook() throws IOException, InvalidFormatException {
  906. if (workbook == null) {
  907. try {
  908. PackagePart worksheetPart = getWorksheetPart();
  909. if (worksheetPart == null) {
  910. workbook = new XSSFWorkbook();
  911. workbook.createSheet();
  912. } else {
  913. workbook = new XSSFWorkbook(worksheetPart.getInputStream());
  914. }
  915. } catch (NotOfficeXmlFileException e) {
  916. workbook = new XSSFWorkbook();
  917. workbook.createSheet();
  918. }
  919. }
  920. return workbook;
  921. }
  922. /**
  923. * while reading chart from template file then we need to parse and store
  924. * embedded excel file in chart object show that we can modify value
  925. * according to use
  926. *
  927. * @param workbook
  928. * workbook object which we read from chart embedded part
  929. * @since POI 4.0.0
  930. */
  931. public void setWorkbook(XSSFWorkbook workbook) {
  932. this.workbook = workbook;
  933. }
  934. /**
  935. * set the relation id of embedded excel relation id into external data
  936. * relation tag
  937. *
  938. * @param id
  939. * relation id of embedded excel relation id into external data
  940. * relation tag
  941. * @since POI 4.0.0
  942. */
  943. public void setExternalId(String id) {
  944. getCTChartSpace().addNewExternalData().setId(id);
  945. }
  946. /**
  947. * @return method return chart index
  948. * @since POI 4.0.0
  949. */
  950. protected int getChartIndex() {
  951. return chartIndex;
  952. }
  953. /**
  954. * set chart index which can be use for relation part
  955. *
  956. * @param chartIndex
  957. * chart index which can be use for relation part
  958. */
  959. public void setChartIndex(int chartIndex) {
  960. this.chartIndex = chartIndex;
  961. }
  962. }