more progress with xssf chart api, see Bug 51196

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1167579 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-09-10 19:00:47 +00:00
parent 57c369a541
commit e4b2a66d09
30 changed files with 826 additions and 644 deletions

View File

@ -20,63 +20,61 @@
package org.apache.poi.xssf.usermodel.examples; package org.apache.poi.xssf.usermodel.examples;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Date;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*; import org.apache.poi.ss.util.*;
import org.apache.poi.ss.usermodel.charts.*; import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.xssf.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/** /**
* Illustrates how to create a simple scatter chart. * Illustrates how to create a simple scatter chart.
*
* @author Roman Kashitsyn
*/ */
public class ScatterChart { public class ScatterChart {
public static void main(String[]args) throws Exception { public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook(); Workbook wb = new XSSFWorkbook();
CreationHelper creationHelper = wb.getCreationHelper(); Sheet sheet = wb.createSheet("Sheet 1");
Sheet sheet = wb.createSheet("Sheet 1"); final int NUM_OF_ROWS = 3;
final int NUM_OF_ROWS = 3; final int NUM_OF_COLUMNS = 10;
final int NUM_OF_COLUMNS = 10;
// Create a row and put some cells in it. Rows are 0 based. // Create a row and put some cells in it. Rows are 0 based.
Row row; Row row;
Cell cell; Cell cell;
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) { for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow((short)rowIndex); row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) { for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short)colIndex); cell = row.createCell((short) colIndex);
cell.setCellValue(colIndex * (rowIndex + 1)); cell.setCellValue(colIndex * (rowIndex + 1));
} }
} }
Drawing drawing = sheet.createDrawingPatriarch(); Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
Chart chart = drawing.createChart(anchor); Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend(); ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.TOP_RIGHT); legend.setPosition(LegendPosition.TOP_RIGHT);
ScatterChartData data = chart.getChartDataFactory().createScatterChartData(); ScatterChartData data = chart.getChartDataFactory().createScatterChartData();
ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
DataMarker xMarker = new DataMarker(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
DataMarker y1Marker = new DataMarker(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
DataMarker y2Marker = new DataMarker(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); ChartDataSource<Number> ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
data.addSerie(xMarker, y1Marker); data.addSerie(xs, ys1);
data.addSerie(xMarker, y2Marker); data.addSerie(xs, ys2);
chart.plot(data, bottomAxis, leftAxis); chart.plot(data, bottomAxis, leftAxis);
// Write the output to a file // Write the output to a file
FileOutputStream fileOut = new FileOutputStream("ooxml-scatter-chart.xlsx"); FileOutputStream fileOut = new FileOutputStream("ooxml-scatter-chart.xlsx");
wb.write(fileOut); wb.write(fileOut);
fileOut.close(); fileOut.close();
} }
} }

View File

@ -17,11 +17,14 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* High level representation of chart axis. * High level representation of chart axis.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ChartAxis { public interface ChartAxis {
/** /**

View File

@ -18,12 +18,14 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.util.Beta;
/** /**
* A factory for different chart axis. * A factory for different chart axis.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ChartAxisFactory { public interface ChartAxisFactory {
/** /**

View File

@ -18,19 +18,21 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.util.Beta;
/** /**
* A base for all chart data types. * A base for all charts data types.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ChartData { public interface ChartData {
/** /**
* Fills a chart with data specified by implementation. * Fills a charts with data specified by implementation.
* *
* @param chart a chart to fill in * @param chart a charts to fill in
* @param axis chart axis to use * @param axis charts axis to use
*/ */
void fillChart(Chart chart, ChartAxis... axis); void fillChart(Chart chart, ChartAxis... axis);
} }

View File

@ -17,11 +17,14 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* A factory for different chart data types. * A factory for different charts data types.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ChartDataFactory { public interface ChartDataFactory {
/** /**

View File

@ -0,0 +1,72 @@
/*
* ====================================================================
* 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.usermodel.charts;
import org.apache.poi.util.Beta;
/**
* Represents data model of the charts.
*
* @param <T> type of points the data source contents
* @author Roman Kashitsyn
*/
@Beta
public interface ChartDataSource<T> {
/**
* Return number of points contained by data source.
*
* @return number of points contained by data source
*/
int getPointCount();
/**
* Returns point value at specified index.
*
* @param index index to value from
* @return point value at specified index.
* @throws {@code IndexOutOfBoundsException} if index
* parameter not in range {@code 0 <= index <= pointCount}
*/
T getPointAt(int index);
/**
* Returns {@code true} if charts data source is valid cell range.
*
* @return {@code true} if charts data source is valid cell range
*/
boolean isReference();
/**
* Returns {@code true} if data source points should be treated as numbers.
*
* @return {@code true} if data source points should be treated as numbers
*/
boolean isNumeric();
/**
* Returns formula representation of the data source. It is only applicable
* for data source that is valid cell range.
*
* @return formula representation of the data source
* @throws {@code UnsupportedOperationException} if the data source is not a
* reference.
*/
String getFormulaString();
}

View File

@ -17,11 +17,14 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* High level representation of chart legend. * High level representation of chart legend.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ChartLegend extends ManuallyPositionable { public interface ChartLegend extends ManuallyPositionable {
/** /**

View File

@ -0,0 +1,146 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Beta;
/**
* Class {@code DataSources} is a factory for {@link ChartDataSource} instances.
*
* @author Roman Kashitsyn
*/
@Beta
public class DataSources {
private DataSources() {
}
public static <T> ChartDataSource<T> fromArray(T[] elements) {
return new ArrayDataSource<T>(elements);
}
public static ChartDataSource<Number> fromNumericCellRange(Sheet sheet, CellRangeAddress cellRangeAddress) {
return new AbstractCellRangeDataSource<Number>(sheet, cellRangeAddress) {
public Number getPointAt(int index) {
CellValue cellValue = getCellValueAt(index);
if (cellValue != null && cellValue.getCellType() == Cell.CELL_TYPE_NUMERIC) {
return Double.valueOf(cellValue.getNumberValue());
} else {
return null;
}
}
public boolean isNumeric() {
return true;
}
};
}
public static ChartDataSource<String> fromStringCellRange(Sheet sheet, CellRangeAddress cellRangeAddress) {
return new AbstractCellRangeDataSource<String>(sheet, cellRangeAddress) {
public String getPointAt(int index) {
CellValue cellValue = getCellValueAt(index);
if (cellValue != null && cellValue.getCellType() == Cell.CELL_TYPE_STRING) {
return cellValue.getStringValue();
} else {
return null;
}
}
public boolean isNumeric() {
return false;
}
};
}
private static class ArrayDataSource<T> implements ChartDataSource<T> {
private final T[] elements;
public ArrayDataSource(T[] elements) {
this.elements = elements;
}
public int getPointCount() {
return elements.length;
}
public T getPointAt(int index) {
return elements[index];
}
public boolean isReference() {
return false;
}
public boolean isNumeric() {
Class<?> arrayComponentType = elements.getClass().getComponentType();
return (Number.class.isAssignableFrom(arrayComponentType));
}
public String getFormulaString() {
throw new UnsupportedOperationException("Literal data source can not be expressed by reference.");
}
}
private abstract static class AbstractCellRangeDataSource<T> implements ChartDataSource<T> {
private final Sheet sheet;
private final CellRangeAddress cellRangeAddress;
private final int numOfCells;
private FormulaEvaluator evaluator;
protected AbstractCellRangeDataSource(Sheet sheet, CellRangeAddress cellRangeAddress) {
this.sheet = sheet;
// Make copy since CellRangeAddress is mutable.
this.cellRangeAddress = cellRangeAddress.copy();
this.numOfCells = this.cellRangeAddress.getNumberOfCells();
this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator();
}
public int getPointCount() {
return numOfCells;
}
public boolean isReference() {
return true;
}
public String getFormulaString() {
return cellRangeAddress.formatAsString(sheet.getSheetName(), true);
}
protected CellValue getCellValueAt(int index) {
if (index < 0 || index >= numOfCells) {
throw new IndexOutOfBoundsException("Index must be between 0 and " +
(numOfCells - 1) + " (inclusive), given: " + index);
}
int firstRow = cellRangeAddress.getFirstRow();
int firstCol = cellRangeAddress.getFirstColumn();
int lastCol = cellRangeAddress.getLastColumn();
int width = lastCol - firstCol + 1;
int rowIndex = firstRow + index / width;
int cellIndex = firstCol + index % width;
Row row = sheet.getRow(rowIndex);
return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex));
}
}
}

View File

@ -17,11 +17,14 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* High level representation of chart element manual layout. * High level representation of chart element manual layout.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ManualLayout { public interface ManualLayout {
/** /**

View File

@ -17,12 +17,15 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* Abstraction of chart element that can be positioned with manual * Abstraction of chart element that can be positioned with manual
* layout. * layout.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ManuallyPositionable { public interface ManuallyPositionable {
/** /**

View File

@ -18,20 +18,17 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import java.util.List; import java.util.List;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.DataMarker;
/** /**
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
public interface ScatterChartData extends ChartData { public interface ScatterChartData extends ChartData {
/** /**
* @param xMarker data marker to be used for X value range * @param xs data source to be used for X axis values
* @param yMarker data marker to be used for Y value range * @param ys data source to be used for Y axis values
* @return a new scatter chart serie * @return a new scatter charts serie
*/ */
ScatterChartSerie addSerie(DataMarker xMarker, DataMarker yMarker); ScatterChartSerie addSerie(ChartDataSource<?> xs, ChartDataSource<? extends Number> ys);
/** /**
* @return list of all series * @return list of all series

View File

@ -17,23 +17,24 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.util.Beta;
import org.apache.poi.ss.util.DataMarker;
import org.apache.poi.ss.usermodel.charts.ChartDataFactory;
/** /**
* Represents scatter charts serie.
*
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ScatterChartSerie { public interface ScatterChartSerie {
/** /**
* @param xMarker data marker to use for X values. * @return data source used for X axis values
*/ */
void setXValues(DataMarker xMarker); ChartDataSource<?> getXValues();
/**' /**
* @param yMarker data marker to use for Y values. * @return data source used for Y axis values
*/ */
void setYValues(DataMarker yMarker); ChartDataSource<? extends Number> getYValues();
} }

View File

@ -17,9 +17,12 @@
package org.apache.poi.ss.usermodel.charts; package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.util.Beta;
/** /**
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public interface ValueAxis extends ChartAxis { public interface ValueAxis extends ChartAxis {
/** /**

View File

@ -1,87 +0,0 @@
/*
* ====================================================================
* 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 org.apache.poi.ss.usermodel.Sheet;
/**
* Represents data marker used in charts.
* @author Roman Kashitsyn
*/
public class DataMarker {
private Sheet sheet;
private CellRangeAddress range;
/**
* @param sheet the sheet where data located.
* @param range the range within that sheet.
*/
public DataMarker(Sheet sheet, CellRangeAddress range) {
this.sheet = sheet;
this.range = range;
}
/**
* Returns the sheet marker points to.
* @return sheet marker points to.
*/
public Sheet getSheet() {
return sheet;
}
/**
* Sets sheet marker points to.
* @param sheet new sheet for the marker.
*/
public void setSheet(Sheet sheet) {
this.sheet = sheet;
}
/**
* Returns range of the marker.
* @return range of cells marker points to.
*/
public CellRangeAddress getRange() {
return range;
}
/**
* Sets range of the marker.
* @param range new range for the marker.
*/
public void setRange(CellRangeAddress range) {
this.range = range;
}
/**
* Formats data marker using canonical format, for example
* `SheetName!$A$1:$A$5'.
* @return formatted data marker.
*/
public String formatAsString() {
String sheetName = (sheet == null) ? (null) : (sheet.getSheetName());
if (range == null) {
return null;
} else {
return range.formatAsString(sheetName, true);
}
}
}

View File

@ -25,115 +25,134 @@ import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
/** /**
* Class that provides useful sheet build capabilities. It can be used * Class {@code SheetBuilder} provides an easy way of building workbook sheets
* in test cases to improve readability or in Swing applications with * from 2D array of Objects. It can be used in test cases to improve code
* tables. * readability or in Swing applications with tables.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
public class SheetBuilder { public class SheetBuilder {
private Workbook workbook; private final Workbook workbook;
private Object[][] cells; private final Object[][] cells;
private boolean shouldCreateEmptyCells = false; private boolean shouldCreateEmptyCells = false;
private String sheetName = null;
public SheetBuilder(Workbook workbook, Object[][] cells) { public SheetBuilder(Workbook workbook, Object[][] cells) {
this.workbook = workbook; this.workbook = workbook;
this.cells = cells; this.cells = cells;
} }
/** /**
* @return true if null objects should be trated as empty cells * Returns {@code true} if null array elements should be treated as empty
* false otherwise * cells.
*
* @return {@code true} if null objects should be treated as empty cells
* and {@code false} otherwise
*/ */
public boolean getCreateEmptyCells() { public boolean getCreateEmptyCells() {
return shouldCreateEmptyCells; return shouldCreateEmptyCells;
} }
/** /**
* @param shouldCreateEmptyCells true if null array elements should be * Specifies if null array elements should be treated as empty cells.
* trated as empty cells *
* @return this * @param shouldCreateEmptyCells {@code true} if null array elements should be
* treated as empty cells
* @return {@code this}
*/ */
public SheetBuilder setCreateEmptyCells(boolean shouldCreateEmptyCells) { public SheetBuilder setCreateEmptyCells(boolean shouldCreateEmptyCells) {
this.shouldCreateEmptyCells = shouldCreateEmptyCells; this.shouldCreateEmptyCells = shouldCreateEmptyCells;
return this; return this;
}
/**
* Specifies name of the sheet to build. If not specified, default name (provided by
* workbook) will be used instead.
* @param sheetName sheet name to use
* @return {@code this}
*/
public SheetBuilder setSheetName(String sheetName) {
this.sheetName = sheetName;
return this;
} }
/** /**
* Builds sheet from parent workbook and 2D array with cell * Builds sheet from parent workbook and 2D array with cell
* values. Creates rows anyway (even if row contains only null * values. Creates rows anyway (even if row contains only null
* cells), creates cells only if corresponding property is true. * cells), creates cells if either corresponding array value is not
* null or createEmptyCells property is true.
* The conversion is performed in the following way: * The conversion is performed in the following way:
* * <p/>
* <ul> * <ul>
* <li>Numbers become numeric cells.</li> * <li>Numbers become numeric cells.</li>
* <li><code>java.util.Date</code> or <code>java.util.Calendar</code> * <li><code>java.util.Date</code> or <code>java.util.Calendar</code>
* instances become date cells.</li> * instances become date cells.</li>
* <li>String with leading '=' char become formulas (leading '=' * <li>String with leading '=' char become formulas (leading '='
* trancated).</li> * will be truncated).</li>
* <li>Other objects become strings via <code>Object.toString()</code> * <li>Other objects become strings via <code>Object.toString()</code>
* method.</li> * method call.</li>
* </ul> * </ul>
* *
* @return newly created sheet * @return newly created sheet
*/ */
public Sheet build() { public Sheet build() {
Sheet sheet = workbook.createSheet(); Sheet sheet = (sheetName == null) ? workbook.createSheet() : workbook.createSheet(sheetName);
Row currentRow = null; Row currentRow = null;
Cell currentCell = null; Cell currentCell = null;
for (int rowIndex = 0; rowIndex < cells.length; ++rowIndex) { for (int rowIndex = 0; rowIndex < cells.length; ++rowIndex) {
Object[] rowArray = cells[rowIndex]; Object[] rowArray = cells[rowIndex];
currentRow = sheet.createRow(rowIndex); currentRow = sheet.createRow(rowIndex);
for (int cellIndex = 0; cellIndex < rowArray.length; ++cellIndex) { for (int cellIndex = 0; cellIndex < rowArray.length; ++cellIndex) {
Object cellValue = rowArray[cellIndex]; Object cellValue = rowArray[cellIndex];
if (cellValue != null || shouldCreateEmptyCells) { if (cellValue != null || shouldCreateEmptyCells) {
currentCell = currentRow.createCell(cellIndex); currentCell = currentRow.createCell(cellIndex);
setCellValue(currentCell, cellValue); setCellValue(currentCell, cellValue);
} }
} }
} }
return sheet; return sheet;
} }
/** /**
* Sets the cell value using object type information. * Sets the cell value using object type information.
* @param cell cell to change *
* @param cell cell to change
* @param value value to set * @param value value to set
*/ */
public void setCellValue(Cell cell, Object value) { private void setCellValue(Cell cell, Object value) {
if (value == null || cell == null) { if (value == null || cell == null) {
return; return;
} else if (value instanceof Number) { } else if (value instanceof Number) {
double doubleValue = ((Number) value).doubleValue(); double doubleValue = ((Number) value).doubleValue();
cell.setCellValue(doubleValue); cell.setCellValue(doubleValue);
} else if (value instanceof Date) { } else if (value instanceof Date) {
cell.setCellValue((Date) value); cell.setCellValue((Date) value);
} else if (value instanceof Calendar) { } else if (value instanceof Calendar) {
cell.setCellValue((Calendar) value); cell.setCellValue((Calendar) value);
} else if (isFormulaDefinition(value)) { } else if (isFormulaDefinition(value)) {
cell.setCellFormula(getFormula(value)); cell.setCellFormula(getFormula(value));
} else { } else {
cell.setCellValue(value.toString()); cell.setCellValue(value.toString());
} }
} }
private boolean isFormulaDefinition(Object obj) { private boolean isFormulaDefinition(Object obj) {
if (obj instanceof String) { if (obj instanceof String) {
String str = (String) obj; String str = (String) obj;
if (str.length() < 2) { if (str.length() < 2) {
return false; return false;
} else { } else {
return ((String) obj).charAt(0) == '='; return ((String) obj).charAt(0) == '=';
} }
} else { } else {
return false; return false;
} }
} }
private String getFormula(Object obj) { private String getFormula(Object obj) {
return ((String) obj).substring(1); return ((String) obj).substring(1);
} }
} }

View File

@ -17,12 +17,10 @@
package org.apache.poi.ss.util.cellwalk; package org.apache.poi.ss.util.cellwalk;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.DataMarker;
/** /**
* Traverse cell range. * Traverse cell range.
@ -35,95 +33,96 @@ public class CellWalk {
private CellRangeAddress range; private CellRangeAddress range;
private boolean traverseEmptyCells; private boolean traverseEmptyCells;
public CellWalk(DataMarker dm) {
this(dm.getSheet(), dm.getRange());
}
public CellWalk(Sheet sheet, CellRangeAddress range) { public CellWalk(Sheet sheet, CellRangeAddress range) {
this.sheet = sheet; this.sheet = sheet;
this.range = range; this.range = range;
this.traverseEmptyCells = false; this.traverseEmptyCells = false;
} }
/** /**
* Should we call handler on empty (blank) cells. Default is * Should we call handler on empty (blank) cells. Default is
* false. * false.
*
* @return true if handler should be called on empty (blank) * @return true if handler should be called on empty (blank)
* cells, false otherwise. * cells, false otherwise.
*/ */
public boolean isTraverseEmptyCells() { public boolean isTraverseEmptyCells() {
return traverseEmptyCells; return traverseEmptyCells;
} }
/** /**
* Sets the traverseEmptyCells property. * Sets the traverseEmptyCells property.
*
* @param traverseEmptyCells new property value * @param traverseEmptyCells new property value
*/ */
public void setTraverseEmptyCells(boolean traverseEmptyCells) { public void setTraverseEmptyCells(boolean traverseEmptyCells) {
this.traverseEmptyCells = traverseEmptyCells; this.traverseEmptyCells = traverseEmptyCells;
} }
/** /**
* Traverse cell range from top left to bottom right cell. * Traverse cell range from top left to bottom right cell.
*
* @param handler handler to call on each appropriate cell * @param handler handler to call on each appropriate cell
*/ */
public void traverse(CellHandler handler) { public void traverse(CellHandler handler) {
int firstRow = range.getFirstRow(); int firstRow = range.getFirstRow();
int lastRow = range.getLastRow(); int lastRow = range.getLastRow();
int firstColumn = range.getFirstColumn(); int firstColumn = range.getFirstColumn();
int lastColumn = range.getLastColumn(); int lastColumn = range.getLastColumn();
final int width = lastColumn - firstColumn + 1; final int width = lastColumn - firstColumn + 1;
SimpleCellWalkContext ctx = new SimpleCellWalkContext(); SimpleCellWalkContext ctx = new SimpleCellWalkContext();
Row currentRow = null; Row currentRow = null;
Cell currentCell = null; Cell currentCell = null;
for (ctx.rowNumber = firstRow; ctx.rowNumber <= lastRow; ++ctx.rowNumber) { for (ctx.rowNumber = firstRow; ctx.rowNumber <= lastRow; ++ctx.rowNumber) {
currentRow = sheet.getRow(ctx.rowNumber); currentRow = sheet.getRow(ctx.rowNumber);
if (currentRow == null) { if (currentRow == null) {
continue; continue;
} }
for (ctx.colNumber = firstColumn; ctx.colNumber <= lastColumn; ++ctx.colNumber) { for (ctx.colNumber = firstColumn; ctx.colNumber <= lastColumn; ++ctx.colNumber) {
currentCell = currentRow.getCell(ctx.colNumber); currentCell = currentRow.getCell(ctx.colNumber);
if (currentCell == null) { if (currentCell == null) {
continue; continue;
} }
if (isEmpty(currentCell) && !traverseEmptyCells) { if (isEmpty(currentCell) && !traverseEmptyCells) {
continue; continue;
} }
ctx.ordinalNumber = ctx.ordinalNumber =
(ctx.rowNumber - firstRow) * width + (ctx.rowNumber - firstRow) * width +
(ctx.colNumber - firstColumn + 1); (ctx.colNumber - firstColumn + 1);
handler.onCell(currentCell, ctx); handler.onCell(currentCell, ctx);
} }
} }
} }
private boolean isEmpty(Cell cell) { private boolean isEmpty(Cell cell) {
return (cell.getCellType() == Cell.CELL_TYPE_BLANK); return (cell.getCellType() == Cell.CELL_TYPE_BLANK);
} }
/** /**
* Inner class to hold walk context. * Inner class to hold walk context.
*
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
private class SimpleCellWalkContext implements CellWalkContext { private class SimpleCellWalkContext implements CellWalkContext {
public long ordinalNumber = 0; public long ordinalNumber = 0;
public int rowNumber = 0; public int rowNumber = 0;
public int colNumber = 0; public int colNumber = 0;
public long getOrdinalNumber() { public long getOrdinalNumber() {
return ordinalNumber; return ordinalNumber;
} }
public int getRowNumber() { public int getRowNumber() {
return rowNumber; return rowNumber;
} }
public int getColumnNumber() { public int getColumnNumber() {
return colNumber; return colNumber;
} }
} }
} }

View File

@ -21,6 +21,7 @@ import org.apache.poi.ss.usermodel.charts.ChartAxis;
import org.apache.poi.ss.usermodel.charts.AxisPosition; import org.apache.poi.ss.usermodel.charts.AxisPosition;
import org.apache.poi.ss.usermodel.charts.AxisOrientation; import org.apache.poi.ss.usermodel.charts.AxisOrientation;
import org.apache.poi.ss.usermodel.charts.AxisCrosses; import org.apache.poi.ss.usermodel.charts.AxisCrosses;
import org.apache.poi.util.Beta;
import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart; import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos;
@ -38,6 +39,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STCrosses;
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public abstract class XSSFChartAxis implements ChartAxis { public abstract class XSSFChartAxis implements ChartAxis {
protected XSSFChart chart; protected XSSFChart chart;

View File

@ -18,10 +18,12 @@
package org.apache.poi.xssf.usermodel.charts; package org.apache.poi.xssf.usermodel.charts;
import org.apache.poi.ss.usermodel.charts.*; import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.util.Beta;
/** /**
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public class XSSFChartDataFactory implements ChartDataFactory { public class XSSFChartDataFactory implements ChartDataFactory {
private static XSSFChartDataFactory instance; private static XSSFChartDataFactory instance;
@ -31,7 +33,7 @@ public class XSSFChartDataFactory implements ChartDataFactory {
} }
/** /**
* @return new scatter chart data instance * @return new scatter charts data instance
*/ */
public XSSFScatterChartData createScatterChartData() { public XSSFScatterChartData createScatterChartData() {
return new XSSFScatterChartData(); return new XSSFScatterChartData();

View File

@ -17,6 +17,7 @@
package org.apache.poi.xssf.usermodel.charts; package org.apache.poi.xssf.usermodel.charts;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.ss.usermodel.charts.ChartLegend; import org.apache.poi.ss.usermodel.charts.ChartLegend;
import org.apache.poi.ss.usermodel.charts.LegendPosition; import org.apache.poi.ss.usermodel.charts.LegendPosition;
@ -30,6 +31,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STLegendPos;
* Represents a SpreadsheetML chart legend * Represents a SpreadsheetML chart legend
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public final class XSSFChartLegend implements ChartLegend { public final class XSSFChartLegend implements ChartLegend {
/** /**

View File

@ -0,0 +1,115 @@
/*
* ====================================================================
* 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.charts.ChartDataSource;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
/**
* Package private class with utility methods.
*
* @author Roman Kashitsyn
*/
class XSSFChartUtil {
private XSSFChartUtil() {}
/**
* Builds CTAxDataSource object content from POI ChartDataSource.
* @param ctAxDataSource OOXML data source to build
* @param dataSource POI data source to use
*/
public static void buildAxDataSource(CTAxDataSource ctAxDataSource, ChartDataSource<?> dataSource) {
if (dataSource.isNumeric()) {
if (dataSource.isReference()) {
buildNumRef(ctAxDataSource.addNewNumRef(), dataSource);
} else {
buildNumLit(ctAxDataSource.addNewNumLit(), dataSource);
}
} else {
if (dataSource.isReference()) {
buildStrRef(ctAxDataSource.addNewStrRef(), dataSource);
} else {
buildStrLit(ctAxDataSource.addNewStrLit(), dataSource);
}
}
}
/**
* Builds CTNumDataSource object content from POI ChartDataSource
* @param ctNumDataSource OOXML data source to build
* @param dataSource POI data source to use
*/
public static void buildNumDataSource(CTNumDataSource ctNumDataSource,
ChartDataSource<? extends Number> dataSource) {
if (dataSource.isReference()) {
buildNumRef(ctNumDataSource.addNewNumRef(), dataSource);
} else {
buildNumLit(ctNumDataSource.addNewNumLit(), dataSource);
}
}
private static void buildNumRef(CTNumRef ctNumRef, ChartDataSource<?> dataSource) {
ctNumRef.setF(dataSource.getFormulaString());
CTNumData cache = ctNumRef.addNewNumCache();
fillNumCache(cache, dataSource);
}
private static void buildNumLit(CTNumData ctNumData, ChartDataSource<?> dataSource) {
fillNumCache(ctNumData, dataSource);
}
private static void buildStrRef(CTStrRef ctStrRef, ChartDataSource<?> dataSource) {
ctStrRef.setF(dataSource.getFormulaString());
CTStrData cache = ctStrRef.addNewStrCache();
fillStringCache(cache, dataSource);
}
private static void buildStrLit(CTStrData ctStrData, ChartDataSource<?> dataSource) {
fillStringCache(ctStrData, dataSource);
}
private static void fillStringCache(CTStrData cache, ChartDataSource<?> dataSource) {
int numOfPoints = dataSource.getPointCount();
cache.addNewPtCount().setVal(numOfPoints);
for (int i = 0; i < numOfPoints; ++i) {
Object value = dataSource.getPointAt(i);
if (value != null) {
CTStrVal ctStrVal = cache.addNewPt();
ctStrVal.setIdx(i);
ctStrVal.setV(value.toString());
}
}
}
private static void fillNumCache(CTNumData cache, ChartDataSource<?> dataSource) {
int numOfPoints = dataSource.getPointCount();
cache.addNewPtCount().setVal(numOfPoints);
for (int i = 0; i < numOfPoints; ++i) {
Number value = (Number) dataSource.getPointAt(i);
if (value != null) {
CTNumVal ctNumVal = cache.addNewPt();
ctNumVal.setIdx(i);
ctNumVal.setV(value.toString());
}
}
}
}

View File

@ -17,6 +17,7 @@
package org.apache.poi.xssf.usermodel.charts; package org.apache.poi.xssf.usermodel.charts;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal; import org.apache.poi.util.Internal;
import org.apache.poi.ss.usermodel.charts.ManualLayout; import org.apache.poi.ss.usermodel.charts.ManualLayout;
import org.apache.poi.ss.usermodel.charts.LayoutMode; import org.apache.poi.ss.usermodel.charts.LayoutMode;
@ -35,6 +36,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget;
* Represents a SpreadsheetML manual layout. * Represents a SpreadsheetML manual layout.
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public final class XSSFManualLayout implements ManualLayout { public final class XSSFManualLayout implements ManualLayout {
/** /**

View File

@ -1,150 +0,0 @@
/* ====================================================================
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;
}
}
}

View File

@ -17,40 +17,25 @@
package org.apache.poi.xssf.usermodel.charts; package org.apache.poi.xssf.usermodel.charts;
import java.util.List;
import java.util.ArrayList;
import org.apache.poi.ss.usermodel.Chart; import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.charts.ChartAxis;
import org.apache.poi.ss.util.DataMarker; import org.apache.poi.ss.usermodel.charts.ChartDataSource;
import org.apache.poi.ss.usermodel.charts.ScatterChartData; import org.apache.poi.ss.usermodel.charts.ScatterChartData;
import org.apache.poi.ss.usermodel.charts.ScatterChartSerie; import org.apache.poi.ss.usermodel.charts.ScatterChartSerie;
import org.apache.poi.ss.usermodel.charts.ChartDataFactory; import org.apache.poi.util.Beta;
import org.apache.poi.ss.usermodel.charts.ChartAxis;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterStyle;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTScatterSer;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxDataSource;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumRef;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumDataSource;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTNumFmt;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx;
import org.openxmlformats.schemas.drawingml.x2006.chart.STScatterStyle;
import org.openxmlformats.schemas.drawingml.x2006.chart.STCrosses;
import org.openxmlformats.schemas.drawingml.x2006.chart.STCrossBetween;
import org.openxmlformats.schemas.drawingml.x2006.chart.STOrientation;
import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos;
import org.openxmlformats.schemas.drawingml.x2006.chart.STAxPos;
import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import java.util.ArrayList;
import java.util.List;
/** /**
* Represents DrawingML scatter chart. * Represents DrawingML scatter charts.
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public class XSSFScatterChartData implements ScatterChartData { public class XSSFScatterChartData implements ScatterChartData {
/** /**
@ -59,118 +44,93 @@ public class XSSFScatterChartData implements ScatterChartData {
private List<Serie> series; private List<Serie> series;
public XSSFScatterChartData() { public XSSFScatterChartData() {
series = new ArrayList<Serie>(); series = new ArrayList<Serie>();
} }
/** /**
* Package private ScatterChartSerie implementation. * Package private ScatterChartSerie implementation.
*/ */
static class Serie implements ScatterChartSerie { static class Serie implements ScatterChartSerie {
private int id; private int id;
private int order; private int order;
private boolean useCache; private ChartDataSource<?> xs;
private DataMarker xMarker; private ChartDataSource<? extends Number> ys;
private DataMarker yMarker;
private XSSFNumberCache lastCaclulatedXCache;
private XSSFNumberCache lastCalculatedYCache;
protected Serie(int id, int order) { protected Serie(int id, int order,
super(); ChartDataSource<?> xs,
this.id = id; ChartDataSource<? extends Number> ys) {
this.order = order; super();
this.useCache = true; this.id = id;
} this.order = order;
this.xs = xs;
this.ys = ys;
}
public void setXValues(DataMarker marker) { /**
xMarker = marker; * Returns data source used for X axis values.
} * @return data source used for X axis values
*/
public ChartDataSource<?> getXValues() {
return xs;
}
public void setYValues(DataMarker marker) { /**
yMarker = marker; * Returns data source used for Y axis values.
} * @return data source used for Y axis values
*/
public ChartDataSource<? extends Number> getYValues() {
return ys;
}
/** protected void addToChart(CTScatterChart ctScatterChart) {
* @param useCache if true, cached results will be added on plot CTScatterSer scatterSer = ctScatterChart.addNewSer();
*/ scatterSer.addNewIdx().setVal(this.id);
public void setUseCache(boolean useCache) { scatterSer.addNewOrder().setVal(this.order);
this.useCache = useCache;
}
/** CTAxDataSource xVal = scatterSer.addNewXVal();
* Returns last calculated number cache for X axis. XSSFChartUtil.buildAxDataSource(xVal, xs);
* @return last calculated number cache for X axis.
*/
XSSFNumberCache getLastCaculatedXCache() {
return lastCaclulatedXCache;
}
/** CTNumDataSource yVal = scatterSer.addNewYVal();
* Returns last calculated number cache for Y axis. XSSFChartUtil.buildNumDataSource(yVal, ys);
* @return last calculated number cache for Y axis. }
*/
XSSFNumberCache getLastCalculatedYCache() {
return lastCalculatedYCache;
}
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) { public ScatterChartSerie addSerie(ChartDataSource<?> xs,
int numOfSeries = series.size(); ChartDataSource<? extends Number> ys) {
Serie newSerie = new Serie(numOfSeries, numOfSeries); if (!ys.isNumeric()) {
newSerie.setXValues(xMarker); throw new IllegalArgumentException("Y axis data source must be numeric.");
newSerie.setYValues(yMarker); }
series.add(newSerie); int numOfSeries = series.size();
return newSerie; Serie newSerie = new Serie(numOfSeries, numOfSeries, xs, ys);
series.add(newSerie);
return newSerie;
} }
public void fillChart(Chart chart, ChartAxis... axis) { public void fillChart(Chart chart, ChartAxis... axis) {
if (!(chart instanceof XSSFChart)) { if (!(chart instanceof XSSFChart)) {
throw new IllegalArgumentException("Chart must be instance of XSSFChart"); throw new IllegalArgumentException("Chart must be instance of XSSFChart");
} }
XSSFChart xssfChart = (XSSFChart) chart; XSSFChart xssfChart = (XSSFChart) chart;
CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea(); CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea();
CTScatterChart scatterChart = plotArea.addNewScatterChart(); CTScatterChart scatterChart = plotArea.addNewScatterChart();
addStyle(scatterChart); addStyle(scatterChart);
for (Serie s : series) { for (Serie s : series) {
s.addToChart(scatterChart); s.addToChart(scatterChart);
} }
for (ChartAxis ax : axis) { for (ChartAxis ax : axis) {
scatterChart.addNewAxId().setVal(ax.getId()); scatterChart.addNewAxId().setVal(ax.getId());
} }
} }
public List<? extends Serie> getSeries() { public List<? extends Serie> getSeries() {
return series; return series;
} }
private void addStyle(CTScatterChart ctScatterChart) { private void addStyle(CTScatterChart ctScatterChart) {
CTScatterStyle scatterStyle = ctScatterChart.addNewScatterStyle(); CTScatterStyle scatterStyle = ctScatterChart.addNewScatterStyle();
scatterStyle.setVal(STScatterStyle.LINE_MARKER); scatterStyle.setVal(STScatterStyle.LINE_MARKER);
} }
} }

View File

@ -24,6 +24,7 @@ import org.apache.poi.ss.usermodel.charts.AxisOrientation;
import org.apache.poi.ss.usermodel.charts.AxisCrossBetween; import org.apache.poi.ss.usermodel.charts.AxisCrossBetween;
import org.apache.poi.ss.usermodel.charts.AxisCrosses; import org.apache.poi.ss.usermodel.charts.AxisCrosses;
import org.apache.poi.util.Beta;
import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos; import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos;
@ -39,6 +40,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos;
* *
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
@Beta
public class XSSFValueAxis extends XSSFChartAxis implements ValueAxis { public class XSSFValueAxis extends XSSFChartAxis implements ValueAxis {
private CTValAx ctValAx; private CTValAx ctValAx;

View File

@ -40,7 +40,7 @@ public final class TestXSSFChartSheet extends TestCase {
XSSFChartSheet sheet = (XSSFChartSheet)wb.getSheetAt(2); XSSFChartSheet sheet = (XSSFChartSheet)wb.getSheetAt(2);
for(Row row : sheet) { for(Row row : sheet) {
fail("Row iterator for chart sheets should return zero rows"); fail("Row iterator for charts sheets should return zero rows");
} }
//access to a arbitrary row //access to a arbitrary row
assertEquals(null, sheet.getRow(1)); assertEquals(null, sheet.getRow(1));

View File

@ -332,7 +332,7 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook {
} }
/** /**
* Problems with XSSFWorkbook.removeSheetAt when workbook contains chart * Problems with XSSFWorkbook.removeSheetAt when workbook contains charts
*/ */
public void testBug47813() { public void testBug47813() {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("47813.xlsx"); XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("47813.xlsx");

View File

@ -1,64 +0,0 @@
/* ====================================================================
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);
}
}

View File

@ -21,42 +21,43 @@ import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress; 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.util.SheetBuilder;
import org.apache.poi.ss.usermodel.charts.*; import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/** /**
* Tests for XSSFScatterChartData. * Tests for XSSFScatterChartData.
*
* @author Roman Kashitsyn * @author Roman Kashitsyn
*/ */
public final class TestXSSFScatterChartData extends TestCase { public final class TestXSSFScatterChartData extends TestCase {
private static Object[][] plotData = new Object[][] { private static final Object[][] plotData = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
{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 { public void testOneSeriePlot() throws Exception {
Workbook wb = new XSSFWorkbook(); Workbook wb = new XSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, plotData).build(); Sheet sheet = new SheetBuilder(wb, plotData).build();
Drawing drawing = sheet.createDrawingPatriarch(); Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30); ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 10, 30);
Chart chart = drawing.createChart(anchor); Chart chart = drawing.createChart(anchor);
ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM); ChartAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM);
ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); ChartAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
ScatterChartData scatterChartData = ScatterChartData scatterChartData =
chart.getChartDataFactory().createScatterChartData(); chart.getChartDataFactory().createScatterChartData();
DataMarker xMarker = new DataMarker(sheet, CellRangeAddress.valueOf("A1:A10")); ChartDataSource<String> xs = DataSources.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1"));
DataMarker yMarker = new DataMarker(sheet, CellRangeAddress.valueOf("B1:B10")); ChartDataSource<Number> ys = DataSources.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2"));
ScatterChartSerie serie = scatterChartData.addSerie(xMarker, yMarker); ScatterChartSerie serie = scatterChartData.addSerie(xs, ys);
assertEquals(1, scatterChartData.getSeries().size()); assertNotNull(serie);
assertEquals(1, scatterChartData.getSeries().size());
assertTrue(scatterChartData.getSeries().contains(serie));
chart.plot(scatterChartData, bottomAxis, leftAxis); chart.plot(scatterChartData, bottomAxis, leftAxis);
} }
} }

View File

@ -0,0 +1,136 @@
/* ====================================================================
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.usermodel.charts;
import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.SheetBuilder;
/**
* Tests for {@link org.apache.poi.ss.usermodel.charts.DataSources}.
*
* @author Roman Kashitsyn
*/
public class TestDataSources extends TestCase {
private static final Object[][] numericCells = {
{0.0, 1.0, 2.0, 3.0, 4.0},
{0.0, "=B1*2", "=C1*2", "=D1*2", "=E1*2"}
};
private static final Object[][] stringCells = {
{ 1, 2, 3, 4, 5},
{"A", "B", "C", "D", "E"}
};
private static final Object[][] mixedCells = {
{1.0, "2.0", 3.0, "4.0", 5.0, "6.0"}
};
public void testNumericArrayDataSource() {
Double[] doubles = new Double[]{1.0, 2.0, 3.0, 4.0, 5.0};
ChartDataSource<Double> doubleDataSource = DataSources.fromArray(doubles);
assertTrue(doubleDataSource.isNumeric());
assertFalse(doubleDataSource.isReference());
assertDataSourceIsEqualToArray(doubleDataSource, doubles);
}
public void testStringArrayDataSource() {
String[] strings = new String[]{"one", "two", "three", "four", "five"};
ChartDataSource<String> stringDataSource = DataSources.fromArray(strings);
assertFalse(stringDataSource.isNumeric());
assertFalse(stringDataSource.isReference());
assertDataSourceIsEqualToArray(stringDataSource, strings);
}
public void testNumericCellDataSource() {
Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, numericCells).build();
CellRangeAddress numCellRange = CellRangeAddress.valueOf("A2:E2");
ChartDataSource<Number> numDataSource = DataSources.fromNumericCellRange(sheet, numCellRange);
assertTrue(numDataSource.isReference());
assertTrue(numDataSource.isNumeric());
assertEquals(numericCells[0].length, numDataSource.getPointCount());
for (int i = 0; i < numericCells[0].length; ++i) {
assertEquals(((Number) numericCells[0][i]).doubleValue() * 2,
numDataSource.getPointAt(i).doubleValue(), 0.00001);
}
}
public void testStringCellDataSource() {
Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, stringCells).build();
CellRangeAddress numCellRange = CellRangeAddress.valueOf("A2:E2");
ChartDataSource<String> numDataSource = DataSources.fromStringCellRange(sheet, numCellRange);
assertTrue(numDataSource.isReference());
assertFalse(numDataSource.isNumeric());
assertEquals(numericCells[0].length, numDataSource.getPointCount());
for (int i = 0; i < stringCells[1].length; ++i) {
assertEquals(stringCells[1][i], numDataSource.getPointAt(i));
}
}
public void testMixedCellDataSource() {
Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, mixedCells).build();
CellRangeAddress mixedCellRange = CellRangeAddress.valueOf("A1:F1");
ChartDataSource<String> strDataSource = DataSources.fromStringCellRange(sheet, mixedCellRange);
ChartDataSource<Number> numDataSource = DataSources.fromNumericCellRange(sheet, mixedCellRange);
for (int i = 0; i < mixedCells[0].length; ++i) {
if (i % 2 == 0) {
assertNull(strDataSource.getPointAt(i));
assertEquals(((Number) mixedCells[0][i]).doubleValue(),
numDataSource.getPointAt(i).doubleValue(), 0.00001);
} else {
assertNull(numDataSource.getPointAt(i));
assertEquals(mixedCells[0][i], strDataSource.getPointAt(i));
}
}
}
public void testIOBExceptionOnInvalidIndex() {
Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, numericCells).build();
CellRangeAddress rangeAddress = CellRangeAddress.valueOf("A2:E2");
ChartDataSource<Number> numDataSource = DataSources.fromNumericCellRange(sheet, rangeAddress);
IndexOutOfBoundsException exception = null;
try {
numDataSource.getPointAt(-1);
} catch (IndexOutOfBoundsException e) {
exception = e;
}
assertNotNull(exception);
exception = null;
try {
numDataSource.getPointAt(numDataSource.getPointCount());
} catch (IndexOutOfBoundsException e) {
exception = e;
}
assertNotNull(exception);
}
private <T> void assertDataSourceIsEqualToArray(ChartDataSource<T> ds, T[] array) {
assertEquals(ds.getPointCount(), array.length);
for (int i = 0; i < array.length; ++i) {
assertEquals(ds.getPointAt(i), array[i]);
}
}
}

View File

@ -29,49 +29,56 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/** /**
* Tests SheetBuilder. * Tests SheetBuilder.
*
* @see org.apache.poi.ss.util.SheetBuilder * @see org.apache.poi.ss.util.SheetBuilder
*/ */
public final class TestSheetBuilder extends TestCase { public final class TestSheetBuilder extends TestCase {
private static Object[][] testData = new Object[][] { private static Object[][] testData = new Object[][]{
{ 1, 2, 3}, {1, 2, 3},
{new Date(), null, null}, {new Date(), null, null},
{ "one", "two", "=A1+B2"} {"one", "two", "=A1+B2"}
}; };
public void testNotCreateEmptyCells() { public void testNotCreateEmptyCells() {
Workbook wb = new HSSFWorkbook(); Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, testData).build(); Sheet sheet = new SheetBuilder(wb, testData).build();
assertEquals(sheet.getPhysicalNumberOfRows(), 3); assertEquals(sheet.getPhysicalNumberOfRows(), 3);
Row firstRow = sheet.getRow(0); Row firstRow = sheet.getRow(0);
Cell firstCell = firstRow.getCell(0); Cell firstCell = firstRow.getCell(0);
assertEquals(firstCell.getCellType(), Cell.CELL_TYPE_NUMERIC); assertEquals(firstCell.getCellType(), Cell.CELL_TYPE_NUMERIC);
assertEquals(1.0, firstCell.getNumericCellValue(), 0.00001); assertEquals(1.0, firstCell.getNumericCellValue(), 0.00001);
Row secondRow = sheet.getRow(1); Row secondRow = sheet.getRow(1);
assertNotNull(secondRow.getCell(0)); assertNotNull(secondRow.getCell(0));
assertNull(secondRow.getCell(2)); assertNull(secondRow.getCell(2));
Row thirdRow = sheet.getRow(2); Row thirdRow = sheet.getRow(2);
assertEquals(Cell.CELL_TYPE_STRING, thirdRow.getCell(0).getCellType()); assertEquals(Cell.CELL_TYPE_STRING, thirdRow.getCell(0).getCellType());
String cellValue = thirdRow.getCell(0).getStringCellValue(); String cellValue = thirdRow.getCell(0).getStringCellValue();
assertEquals(testData[2][0].toString(), cellValue); assertEquals(testData[2][0].toString(), cellValue);
assertEquals(Cell.CELL_TYPE_FORMULA, thirdRow.getCell(2).getCellType()); assertEquals(Cell.CELL_TYPE_FORMULA, thirdRow.getCell(2).getCellType());
assertEquals("A1+B2", thirdRow.getCell(2).getCellFormula()); assertEquals("A1+B2", thirdRow.getCell(2).getCellFormula());
} }
public void testEmptyCells() { public void testEmptyCells() {
Workbook wb = new HSSFWorkbook(); Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, testData).setCreateEmptyCells(true).build(); Sheet sheet = new SheetBuilder(wb, testData).setCreateEmptyCells(true).build();
Cell emptyCell = sheet.getRow(1).getCell(1); Cell emptyCell = sheet.getRow(1).getCell(1);
assertNotNull(emptyCell); assertNotNull(emptyCell);
assertEquals(Cell.CELL_TYPE_BLANK, emptyCell.getCellType()); assertEquals(Cell.CELL_TYPE_BLANK, emptyCell.getCellType());
} }
public void testSheetName() {
final String sheetName = "TEST SHEET NAME";
Workbook wb = new HSSFWorkbook();
Sheet sheet = new SheetBuilder(wb, testData).setSheetName(sheetName).build();
assertEquals(sheetName, sheet.getSheetName());
}
} }