From 0a86816c7c851f821ce9800248b64afb1f4f99c4 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Wed, 20 Jul 2011 08:03:23 +0000 Subject: [PATCH] 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 --- .../org/apache/poi/ss/util/SheetBuilder.java | 139 +++++++++++++ .../poi/ss/util/cellwalk/CellHandler.java | 34 ++++ .../apache/poi/ss/util/cellwalk/CellWalk.java | 129 ++++++++++++ .../poi/ss/util/cellwalk/CellWalkContext.java | 52 +++++ .../usermodel/charts/XSSFNumberCache.java | 150 ++++++++++++++ .../charts/XSSFScatterChartData.java | 185 ++++++++++-------- .../usermodel/charts/TestXSSFNumberCache.java | 64 ++++++ .../charts/TestXSSFScatterChartData.java | 52 +++-- .../apache/poi/ss/util/TestSheetBuilder.java | 77 ++++++++ .../poi/ss/util/cellwalk/TestCellWalk.java | 78 ++++++++ 10 files changed, 862 insertions(+), 98 deletions(-) create mode 100644 src/java/org/apache/poi/ss/util/SheetBuilder.java create mode 100644 src/java/org/apache/poi/ss/util/cellwalk/CellHandler.java create mode 100644 src/java/org/apache/poi/ss/util/cellwalk/CellWalk.java create mode 100644 src/java/org/apache/poi/ss/util/cellwalk/CellWalkContext.java create mode 100644 src/ooxml/java/org/apache/poi/xssf/usermodel/charts/XSSFNumberCache.java create mode 100644 src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFNumberCache.java create mode 100644 src/testcases/org/apache/poi/ss/util/TestSheetBuilder.java create mode 100644 src/testcases/org/apache/poi/ss/util/cellwalk/TestCellWalk.java diff --git a/src/java/org/apache/poi/ss/util/SheetBuilder.java b/src/java/org/apache/poi/ss/util/SheetBuilder.java new file mode 100644 index 0000000000..31609001fd --- /dev/null +++ b/src/java/org/apache/poi/ss/util/SheetBuilder.java @@ -0,0 +1,139 @@ +/* ==================================================================== + 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.ss.util; + +import java.util.Calendar; +import java.util.Date; + +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Cell; + +/** + * Class that provides useful sheet build capabilities. It can be used + * in test cases to improve readability or in Swing applications with + * tables. + * + * @author Roman Kashitsyn + */ +public class SheetBuilder { + + private Workbook workbook; + private Object[][] cells; + private boolean shouldCreateEmptyCells = false; + + public SheetBuilder(Workbook workbook, Object[][] cells) { + this.workbook = workbook; + this.cells = cells; + } + + /** + * @return true if null objects should be trated as empty cells + * false otherwise + */ + public boolean getCreateEmptyCells() { + return shouldCreateEmptyCells; + } + + /** + * @param shouldCreateEmptyCells true if null array elements should be + * trated as empty cells + * @return this + */ + public SheetBuilder setCreateEmptyCells(boolean shouldCreateEmptyCells) { + this.shouldCreateEmptyCells = shouldCreateEmptyCells; + return this; + } + + /** + * Builds sheet from parent workbook and 2D array with cell + * values. Creates rows anyway (even if row contains only null + * cells), creates cells only if corresponding property is true. + * The conversion is performed in the following way: + * + * + * + * @return newly created sheet + */ + public Sheet build() { + Sheet sheet = workbook.createSheet(); + Row currentRow = null; + Cell currentCell = null; + + for (int rowIndex = 0; rowIndex < cells.length; ++rowIndex) { + Object[] rowArray = cells[rowIndex]; + currentRow = sheet.createRow(rowIndex); + + for (int cellIndex = 0; cellIndex < rowArray.length; ++cellIndex) { + Object cellValue = rowArray[cellIndex]; + if (cellValue != null || shouldCreateEmptyCells) { + currentCell = currentRow.createCell(cellIndex); + setCellValue(currentCell, cellValue); + } + } + } + return sheet; + } + + /** + * Sets the cell value using object type information. + * @param cell cell to change + * @param value value to set + */ + public void setCellValue(Cell cell, Object value) { + if (value == null || cell == null) { + return; + } else if (value instanceof Number) { + double doubleValue = ((Number) value).doubleValue(); + cell.setCellValue(doubleValue); + } else if (value instanceof Date) { + cell.setCellValue((Date) value); + } else if (value instanceof Calendar) { + cell.setCellValue((Calendar) value); + } else if (isFormulaDefinition(value)) { + cell.setCellFormula(getFormula(value)); + } else { + cell.setCellValue(value.toString()); + } + } + + private boolean isFormulaDefinition(Object obj) { + if (obj instanceof String) { + String str = (String) obj; + if (str.length() < 2) { + return false; + } else { + return ((String) obj).charAt(0) == '='; + } + } else { + return false; + } + } + + private String getFormula(Object obj) { + return ((String) obj).substring(1); + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/ss/util/cellwalk/CellHandler.java b/src/java/org/apache/poi/ss/util/cellwalk/CellHandler.java new file mode 100644 index 0000000000..9fb4542c7d --- /dev/null +++ b/src/java/org/apache/poi/ss/util/cellwalk/CellHandler.java @@ -0,0 +1,34 @@ +/* ==================================================================== + 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.ss.util.cellwalk; + +import org.apache.poi.ss.usermodel.Cell; + +/** + * Represents callback for CellWalk traverse method. + * @author Roman Kashitsyn + */ +public interface CellHandler { + + /** + * @param cell current cell + * @param ctx information about invokation context + */ + void onCell(Cell cell, CellWalkContext ctx); + +} \ No newline at end of file diff --git a/src/java/org/apache/poi/ss/util/cellwalk/CellWalk.java b/src/java/org/apache/poi/ss/util/cellwalk/CellWalk.java new file mode 100644 index 0000000000..53e21640cf --- /dev/null +++ b/src/java/org/apache/poi/ss/util/cellwalk/CellWalk.java @@ -0,0 +1,129 @@ +/* ==================================================================== + 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.ss.util.cellwalk; + + +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.util.CellRangeAddress; +import org.apache.poi.ss.util.DataMarker; + +/** + * Traverse cell range. + * + * @author Roman Kashitsyn + */ +public class CellWalk { + + private Sheet sheet; + private CellRangeAddress range; + private boolean traverseEmptyCells; + + public CellWalk(DataMarker dm) { + this(dm.getSheet(), dm.getRange()); + } + + public CellWalk(Sheet sheet, CellRangeAddress range) { + this.sheet = sheet; + this.range = range; + this.traverseEmptyCells = false; + } + + /** + * Should we call handler on empty (blank) cells. Default is + * false. + * @return true if handler should be called on empty (blank) + * cells, false otherwise. + */ + public boolean isTraverseEmptyCells() { + return traverseEmptyCells; + } + + /** + * Sets the traverseEmptyCells property. + * @param traverseEmptyCells new property value + */ + public void setTraverseEmptyCells(boolean traverseEmptyCells) { + this.traverseEmptyCells = traverseEmptyCells; + } + + /** + * Traverse cell range from top left to bottom right cell. + * @param handler handler to call on each appropriate cell + */ + public void traverse(CellHandler handler) { + int firstRow = range.getFirstRow(); + int lastRow = range.getLastRow(); + int firstColumn = range.getFirstColumn(); + int lastColumn = range.getLastColumn(); + final int width = lastColumn - firstColumn + 1; + SimpleCellWalkContext ctx = new SimpleCellWalkContext(); + Row currentRow = null; + Cell currentCell = null; + + for (ctx.rowNumber = firstRow; ctx.rowNumber <= lastRow; ++ctx.rowNumber) { + currentRow = sheet.getRow(ctx.rowNumber); + if (currentRow == null) { + continue; + } + for (ctx.colNumber = firstColumn; ctx.colNumber <= lastColumn; ++ctx.colNumber) { + currentCell = currentRow.getCell(ctx.colNumber); + + if (currentCell == null) { + continue; + } + if (isEmpty(currentCell) && !traverseEmptyCells) { + continue; + } + + ctx.ordinalNumber = + (ctx.rowNumber - firstRow) * width + + (ctx.colNumber - firstColumn + 1); + + handler.onCell(currentCell, ctx); + } + } + } + + private boolean isEmpty(Cell cell) { + return (cell.getCellType() == Cell.CELL_TYPE_BLANK); + } + + /** + * Inner class to hold walk context. + * @author Roman Kashitsyn + */ + private class SimpleCellWalkContext implements CellWalkContext { + public long ordinalNumber = 0; + public int rowNumber = 0; + public int colNumber = 0; + + public long getOrdinalNumber() { + return ordinalNumber; + } + + public int getRowNumber() { + return rowNumber; + } + + public int getColumnNumber() { + return colNumber; + } + } +} diff --git a/src/java/org/apache/poi/ss/util/cellwalk/CellWalkContext.java b/src/java/org/apache/poi/ss/util/cellwalk/CellWalkContext.java new file mode 100644 index 0000000000..f22bd6f584 --- /dev/null +++ b/src/java/org/apache/poi/ss/util/cellwalk/CellWalkContext.java @@ -0,0 +1,52 @@ +/* ==================================================================== + 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.ss.util.cellwalk; + +/** + * @author Roman Kashitsyn + */ +public interface CellWalkContext { + + /** + * Returns ordinal number of cell in range. Numeration starts + * from top left cell and ends at bottom right cell. Here is a + * brief example (number in cell is it's ordinal number): + * + * + * + * + * + * + *
12
34
+ * + * @return ordinal number of current cell + */ + long getOrdinalNumber(); + + /** + * Returns number of current row. + * @return number of current row + */ + int getRowNumber(); + + /** + * Returns number of current column. + * @return number of current column + */ + int getColumnNumber(); +} \ No newline at end of file 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 series; - - public XSSFScatterChartData() { - series = new ArrayList(); + /** + * List of all data series. + */ + private List series; + + public XSSFScatterChartData() { + series = new ArrayList(); + } + + /** + * 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 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 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); + } } diff --git a/src/testcases/org/apache/poi/ss/util/TestSheetBuilder.java b/src/testcases/org/apache/poi/ss/util/TestSheetBuilder.java new file mode 100644 index 0000000000..edb3df9355 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/util/TestSheetBuilder.java @@ -0,0 +1,77 @@ +/* ==================================================================== + 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.ss.util; + +import java.util.Date; + +import junit.framework.TestCase; + +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; + +/** + * Tests SheetBuilder. + * @see org.apache.poi.ss.util.SheetBuilder + */ +public final class TestSheetBuilder extends TestCase { + + private static Object[][] testData = new Object[][] { + { 1, 2, 3}, + {new Date(), null, null}, + { "one", "two", "=A1+B2"} + }; + + public void testNotCreateEmptyCells() { + Workbook wb = new HSSFWorkbook(); + Sheet sheet = new SheetBuilder(wb, testData).build(); + + assertEquals(sheet.getPhysicalNumberOfRows(), 3); + + Row firstRow = sheet.getRow(0); + Cell firstCell = firstRow.getCell(0); + + assertEquals(firstCell.getCellType(), Cell.CELL_TYPE_NUMERIC); + assertEquals(1.0, firstCell.getNumericCellValue(), 0.00001); + + + Row secondRow = sheet.getRow(1); + assertNotNull(secondRow.getCell(0)); + assertNull(secondRow.getCell(2)); + + Row thirdRow = sheet.getRow(2); + assertEquals(Cell.CELL_TYPE_STRING, thirdRow.getCell(0).getCellType()); + String cellValue = thirdRow.getCell(0).getStringCellValue(); + assertEquals(testData[2][0].toString(), cellValue); + + assertEquals(Cell.CELL_TYPE_FORMULA, thirdRow.getCell(2).getCellType()); + assertEquals("A1+B2", thirdRow.getCell(2).getCellFormula()); + } + + public void testEmptyCells() { + Workbook wb = new HSSFWorkbook(); + Sheet sheet = new SheetBuilder(wb, testData).setCreateEmptyCells(true).build(); + + Cell emptyCell = sheet.getRow(1).getCell(1); + assertNotNull(emptyCell); + assertEquals(Cell.CELL_TYPE_BLANK, emptyCell.getCellType()); + } + +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/ss/util/cellwalk/TestCellWalk.java b/src/testcases/org/apache/poi/ss/util/cellwalk/TestCellWalk.java new file mode 100644 index 0000000000..471f36e7ec --- /dev/null +++ b/src/testcases/org/apache/poi/ss/util/cellwalk/TestCellWalk.java @@ -0,0 +1,78 @@ +/* ==================================================================== + 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.ss.util.cellwalk; + +import junit.framework.TestCase; + +import java.util.Date; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.SheetBuilder; + +public class TestCellWalk extends TestCase { + + private static Object[][] testData = new Object[][] { + { 1, 2, null}, + {null, new Date(), null}, + {null, null, "str"} + }; + + private CountCellHandler countCellHandler = new CountCellHandler(); + + public void testNotTraverseEmptyCells() { + Workbook wb = new HSSFWorkbook(); + Sheet sheet = new SheetBuilder(wb, testData).build(); + CellRangeAddress range = CellRangeAddress.valueOf("A1:C3"); + + CellWalk cellWalk = new CellWalk(sheet, range); + countCellHandler.reset(); + cellWalk.traverse(countCellHandler); + + assertEquals(4, countCellHandler.getVisitedCellsNumber()); + /* 1 + 2 + 5 + 9 */ + assertEquals(17L, countCellHandler.getOrdinalNumberSum()); + } + + + private static class CountCellHandler implements CellHandler { + + private int cellsVisited = 0; + private long ordinalNumberSum = 0L; + + public void onCell(Cell cell, CellWalkContext ctx) { + ++cellsVisited; + ordinalNumberSum += ctx.getOrdinalNumber(); + } + + public int getVisitedCellsNumber() { + return cellsVisited; + } + + public long getOrdinalNumberSum() { + return ordinalNumberSum; + } + + public void reset() { + cellsVisited = 0; + ordinalNumberSum = 0L; + } + } +} \ No newline at end of file -- 2.39.5