diff options
author | Yegor Kozlov <yegor@apache.org> | 2011-07-20 08:03:23 +0000 |
---|---|---|
committer | Yegor Kozlov <yegor@apache.org> | 2011-07-20 08:03:23 +0000 |
commit | 0a86816c7c851f821ce9800248b64afb1f4f99c4 (patch) | |
tree | 4ce549ab330b08d7c4b5edba11733198ff9a79d5 /src/ooxml | |
parent | cbb80e2943a1fdcf22ca8c969efd0e93b048f1e7 (diff) | |
download | poi-0a86816c7c851f821ce9800248b64afb1f4f99c4.tar.gz poi-0a86816c7c851f821ce9800248b64afb1f4f99c4.zip |
more progress with support for charts in XSSF, see patch 51196
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1148642 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/ooxml')
4 files changed, 353 insertions, 98 deletions
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFNumberCache.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFNumberCache.java new file mode 100644 index 0000000000..04902049e3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFNumberCache.java @@ -0,0 +1,150 @@ +/* ==================================================================== + 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.xssf.usermodel.charts; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.CellValue; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.NumberToTextConverter; +import org.apache.poi.ss.util.DataMarker; +import org.apache.poi.ss.util.cellwalk.CellWalk; +import org.apache.poi.ss.util.cellwalk.CellHandler; +import org.apache.poi.ss.util.cellwalk.CellWalkContext; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumData; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumVal; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTUnsignedInt; + +/** + * Package private class to fill chart's number reference with cached + * numeric values. If a formula-typed cell referenced by data marker, + * cell's value will be calculated and placed to cache. Numeric cells + * will be placed to cache as is. Non-numeric cells will be ignored. + * + * @author Roman Kashitsyn + */ +class XSSFNumberCache { + + private CTNumData ctNumData; + + XSSFNumberCache(CTNumData ctNumData) { + this.ctNumData = ctNumData; + } + + /** + * Builds new numeric cache container. + * @param marker data marker to use for cache evaluation + * @param ctNumRef parent number reference + * @return numeric cache instance + */ + static XSSFNumberCache buildCache(DataMarker marker, CTNumRef ctNumRef) { + CellRangeAddress range = marker.getRange(); + int numOfPoints = range.getNumberOfCells(); + + if (numOfPoints == 0) { + // Nothing to do. + return null; + } + + XSSFNumberCache cache = new XSSFNumberCache(ctNumRef.addNewNumCache()); + cache.setPointCount(numOfPoints); + + Workbook wb = marker.getSheet().getWorkbook(); + FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); + + CellWalk cellWalk = new CellWalk(marker); + NumCacheCellHandler numCacheHandler = cache.new NumCacheCellHandler(evaluator); + cellWalk.traverse(numCacheHandler); + return cache; + } + + /** + * Returns total count of points in cache. Some (or even all) of + * them might be empty. + * @return total count of points in cache + */ + long getPointCount() { + CTUnsignedInt pointCount = ctNumData.getPtCount(); + if (pointCount != null) { + return pointCount.getVal(); + } else { + return 0L; + } + } + + /** + * Returns cache value at specified index. + * @param index index of the point in cache + * @return point value + */ + double getValueAt(int index) { + /* TODO: consider more effective algorithm. Left as is since + * this method should be invoked mostly in tests. */ + for (CTNumVal pt : ctNumData.getPtList()) { + if (pt.getIdx() == index) { + return Double.valueOf(pt.getV()).doubleValue(); + } + } + return 0.0; + } + + private void setPointCount(int numOfPoints) { + ctNumData.addNewPtCount().setVal(numOfPoints); + } + + private class NumCacheCellHandler implements CellHandler { + + private FormulaEvaluator evaluator; + + public NumCacheCellHandler(FormulaEvaluator evaluator) { + this.evaluator = evaluator; + } + + public void onCell(Cell cell, CellWalkContext ctx) { + double pointValue = getOrEvalCellValue(cell); + /* Silently ignore non-numeric values. + * This is Office default behaviour. */ + if (Double.isNaN(pointValue)) { + return; + } + + CTNumVal point = ctNumData.addNewPt(); + point.setIdx(ctx.getOrdinalNumber()); + point.setV(NumberToTextConverter.toText(pointValue)); + } + + private double getOrEvalCellValue(Cell cell) { + int cellType = cell.getCellType(); + + if (cellType == Cell.CELL_TYPE_NUMERIC) { + return cell.getNumericCellValue(); + } else if (cellType == Cell.CELL_TYPE_FORMULA) { + CellValue value = evaluator.evaluate(cell); + if (value.getCellType() == Cell.CELL_TYPE_NUMERIC) { + return value.getNumberValue(); + } + } + return Double.NaN; + } + + } +} 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 42b2775f22..36959b2d4d 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 @@ -6,14 +6,14 @@ (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 + 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.xssf.usermodel.charts; @@ -53,93 +53,124 @@ import org.apache.poi.xssf.usermodel.XSSFChart; */ public class XSSFScatterChartData implements ScatterChartData { - /** - * List of all data series. - */ - private List<Serie> series; - - public XSSFScatterChartData() { - series = new ArrayList<Serie>(); + /** + * List of all data series. + */ + private List<Serie> series; + + public XSSFScatterChartData() { + series = new ArrayList<Serie>(); + } + + /** + * Package private ScatterChartSerie implementation. + */ + static class Serie implements ScatterChartSerie { + private int id; + private int order; + private boolean useCache; + private DataMarker xMarker; + private DataMarker yMarker; + private XSSFNumberCache lastCaclulatedXCache; + private XSSFNumberCache lastCalculatedYCache; + + protected Serie(int id, int order) { + super(); + this.id = id; + this.order = order; + this.useCache = true; } - public static class Serie implements ScatterChartSerie { - private int id; - private int order; - private boolean useCache; - private DataMarker xMarker; - private DataMarker yMarker; - - public Serie(int id, int order) { - super(); - this.id = id; - this.order = order; - this.useCache = false; - } - - public void setXValues(DataMarker marker) { - xMarker = marker; - } - - public void setYValues(DataMarker marker) { - yMarker = marker; - } - - /** - * @param useCache if true, cached results will be added on plot - */ - public void setUseCache(boolean useCache) { - this.useCache = useCache; - } - - protected void addToChart(CTScatterChart ctScatterChart) { - CTScatterSer scatterSer = ctScatterChart.addNewSer(); - scatterSer.addNewIdx().setVal(this.id); - scatterSer.addNewOrder().setVal(this.order); - - CTAxDataSource xVal = scatterSer.addNewXVal(); - CTNumRef numRef = xVal.addNewNumRef(); - numRef.setF(xMarker.formatAsString()); - - CTNumDataSource yVal = scatterSer.addNewYVal(); - numRef = yVal.addNewNumRef(); - numRef.setF(yMarker.formatAsString()); - } + public void setXValues(DataMarker marker) { + xMarker = marker; } - public XSSFScatterChartData.Serie addSerie(DataMarker xMarker, DataMarker yMarker) { - int numOfSeries = series.size(); - Serie newSerie = new Serie(numOfSeries, numOfSeries); - newSerie.setXValues(xMarker); - newSerie.setYValues(yMarker); - series.add(newSerie); - return newSerie; + public void setYValues(DataMarker marker) { + yMarker = marker; } - public void fillChart(Chart chart, ChartAxis... axis) { - if (!(chart instanceof XSSFChart)) { - throw new IllegalArgumentException("Chart must be instance of XSSFChart"); - } + /** + * @param useCache if true, cached results will be added on plot + */ + public void setUseCache(boolean useCache) { + this.useCache = useCache; + } - XSSFChart xssfChart = (XSSFChart) chart; - CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea(); - CTScatterChart scatterChart = plotArea.addNewScatterChart(); - addStyle(scatterChart); + /** + * Returns last calculated number cache for X axis. + * @return last calculated number cache for X axis. + */ + XSSFNumberCache getLastCaculatedXCache() { + return lastCaclulatedXCache; + } - for (Serie s : series) { - s.addToChart(scatterChart); - } + /** + * Returns last calculated number cache for Y axis. + * @return last calculated number cache for Y axis. + */ + XSSFNumberCache getLastCalculatedYCache() { + return lastCalculatedYCache; + } - for (ChartAxis ax : axis) { - scatterChart.addNewAxId().setVal(ax.getId()); - } + protected void addToChart(CTScatterChart ctScatterChart) { + CTScatterSer scatterSer = ctScatterChart.addNewSer(); + scatterSer.addNewIdx().setVal(this.id); + scatterSer.addNewOrder().setVal(this.order); + + /* TODO: add some logic to automatically recognize cell + * types and choose appropriate data representation for + * X axis. + */ + CTAxDataSource xVal = scatterSer.addNewXVal(); + CTNumRef xNumRef = xVal.addNewNumRef(); + xNumRef.setF(xMarker.formatAsString()); + + CTNumDataSource yVal = scatterSer.addNewYVal(); + CTNumRef yNumRef = yVal.addNewNumRef(); + yNumRef.setF(yMarker.formatAsString()); + + if (useCache) { + /* We can not store cache since markers are not immutable */ + XSSFNumberCache.buildCache(xMarker, xNumRef); + lastCalculatedYCache = XSSFNumberCache.buildCache(yMarker, yNumRef); + } } + } + + public ScatterChartSerie addSerie(DataMarker xMarker, DataMarker yMarker) { + int numOfSeries = series.size(); + Serie newSerie = new Serie(numOfSeries, numOfSeries); + newSerie.setXValues(xMarker); + newSerie.setYValues(yMarker); + series.add(newSerie); + return newSerie; + } + + public void fillChart(Chart chart, ChartAxis... axis) { + if (!(chart instanceof XSSFChart)) { + throw new IllegalArgumentException("Chart must be instance of XSSFChart"); + } + + XSSFChart xssfChart = (XSSFChart) chart; + CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea(); + CTScatterChart scatterChart = plotArea.addNewScatterChart(); + addStyle(scatterChart); - public List<? extends Serie> getSeries() { - return series; + for (Serie s : series) { + s.addToChart(scatterChart); } - private void addStyle(CTScatterChart ctScatterChart) { - CTScatterStyle scatterStyle = ctScatterChart.addNewScatterStyle(); - scatterStyle.setVal(STScatterStyle.LINE_MARKER); + for (ChartAxis ax : axis) { + scatterChart.addNewAxId().setVal(ax.getId()); } + } + + public List<? extends Serie> getSeries() { + return series; + } + + private void addStyle(CTScatterChart ctScatterChart) { + CTScatterStyle scatterStyle = ctScatterChart.addNewScatterStyle(); + scatterStyle.setVal(STScatterStyle.LINE_MARKER); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFNumberCache.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFNumberCache.java new file mode 100644 index 0000000000..94621de825 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFNumberCache.java @@ -0,0 +1,64 @@ +/* ==================================================================== + 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.xssf.usermodel.charts; + +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.DataMarker; +import org.apache.poi.ss.util.SheetBuilder; +import org.apache.poi.ss.usermodel.charts.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + + +import junit.framework.TestCase; + +public class TestXSSFNumberCache extends TestCase { + private static Object[][] plotData = { + {0, 1, 2, 3, 4}, + {0, "=B1*2", "=C1*2", "=D1*2", "=E1*4"} + }; + + public void testFormulaCache() { + 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); + + ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); + ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + + ScatterChartData scatterChartData = + chart.getChartDataFactory().createScatterChartData(); + + DataMarker xMarker = new DataMarker(sheet, CellRangeAddress.valueOf("A1:E1")); + DataMarker yMarker = new DataMarker(sheet, CellRangeAddress.valueOf("A2:E2")); + ScatterChartSerie serie = scatterChartData.addSerie(xMarker, yMarker); + + chart.plot(scatterChartData, bottomAxis, leftAxis); + + XSSFScatterChartData.Serie xssfScatterSerie = + (XSSFScatterChartData.Serie) serie; + XSSFNumberCache yCache = xssfScatterSerie.getLastCalculatedYCache(); + + assertEquals(5, yCache.getPointCount()); + assertEquals(4.0, yCache.getValueAt(3), 0.00001); + assertEquals(16.0, yCache.getValueAt(5), 0.00001); + } + + +}
\ No newline at end of file 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 852af700fd..4a3b9c0f43 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 @@ -6,47 +6,57 @@ (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 + 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.xssf.usermodel.charts; import junit.framework.TestCase; -import org.apache.poi.xssf.usermodel.*; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.DataMarker; +import org.apache.poi.ss.util.SheetBuilder; import org.apache.poi.ss.usermodel.charts.*; -import org.apache.poi.xssf.usermodel.charts.XSSFChartDataFactory; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +/** + * Tests for XSSFScatterChartData. + * @author Roman Kashitsyn + */ public final class TestXSSFScatterChartData extends TestCase { - - public void testOneSeriePlot() 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); - ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); - ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + private static Object[][] plotData = new Object[][] { + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + }; + + public void testOneSeriePlot() throws Exception { + 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); - ScatterChartData scatterChartData = - XSSFChartDataFactory.getInstance().createScatterChartData(); + ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); + ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); - DataMarker xMarker = new DataMarker(sheet, new CellRangeAddress(0,0,1,10)); - DataMarker yMarker = new DataMarker(sheet, new CellRangeAddress(1,1,1,10)); - ScatterChartSerie serie = scatterChartData.addSerie(xMarker, yMarker); + ScatterChartData scatterChartData = + chart.getChartDataFactory().createScatterChartData(); - assertEquals(scatterChartData.getSeries().size(), 1); + DataMarker xMarker = new DataMarker(sheet, CellRangeAddress.valueOf("A1:A10")); + DataMarker yMarker = new DataMarker(sheet, CellRangeAddress.valueOf("B1:B10")); + ScatterChartSerie serie = scatterChartData.addSerie(xMarker, yMarker); - chart.plot(scatterChartData, bottomAxis, leftAxis); - } + assertEquals(1, scatterChartData.getSeries().size()); + + chart.plot(scatterChartData, bottomAxis, leftAxis); + } } |