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,23 +20,20 @@
package org.apache.poi.xssf.usermodel.examples;
import java.io.FileOutputStream;
import java.util.Date;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
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;
/**
* Illustrates how to create a simple scatter chart.
*
* @author Roman Kashitsyn
*/
public class ScatterChart {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
CreationHelper creationHelper = wb.getCreationHelper();
Sheet sheet = wb.createSheet("Sheet 1");
final int NUM_OF_ROWS = 3;
final int NUM_OF_COLUMNS = 10;
@ -63,14 +60,15 @@ public class ScatterChart {
ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM);
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));
DataMarker y1Marker = new DataMarker(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> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 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(xMarker, y2Marker);
data.addSerie(xs, ys1);
data.addSerie(xs, ys2);
chart.plot(data, bottomAxis, leftAxis);

View File

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

View File

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

View File

@ -18,19 +18,21 @@
package org.apache.poi.ss.usermodel.charts;
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
*/
@Beta
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 axis chart axis to use
* @param chart a charts to fill in
* @param axis charts axis to use
*/
void fillChart(Chart chart, ChartAxis... axis);
}

View File

@ -17,11 +17,14 @@
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
*/
@Beta
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;
import org.apache.poi.util.Beta;
/**
* High level representation of chart legend.
*
* @author Roman Kashitsyn
*/
@Beta
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;
import org.apache.poi.util.Beta;
/**
* High level representation of chart element manual layout.
*
* @author Roman Kashitsyn
*/
@Beta
public interface ManualLayout {
/**

View File

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

View File

@ -18,20 +18,17 @@
package org.apache.poi.ss.usermodel.charts;
import java.util.List;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.DataMarker;
/**
* @author Roman Kashitsyn
*/
public interface ScatterChartData extends ChartData {
/**
* @param xMarker data marker to be used for X value range
* @param yMarker data marker to be used for Y value range
* @return a new scatter chart serie
* @param xs data source to be used for X axis values
* @param ys data source to be used for Y axis values
* @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

View File

@ -17,23 +17,24 @@
package org.apache.poi.ss.usermodel.charts;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.DataMarker;
import org.apache.poi.ss.usermodel.charts.ChartDataFactory;
import org.apache.poi.util.Beta;
/**
* Represents scatter charts serie.
*
* @author Roman Kashitsyn
*/
@Beta
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;
import org.apache.poi.util.Beta;
/**
* @author Roman Kashitsyn
*/
@Beta
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,17 +25,18 @@ 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.
* Class {@code SheetBuilder} provides an easy way of building workbook sheets
* from 2D array of Objects. It can be used in test cases to improve code
* readability or in Swing applications with tables.
*
* @author Roman Kashitsyn
*/
public class SheetBuilder {
private Workbook workbook;
private Object[][] cells;
private final Workbook workbook;
private final Object[][] cells;
private boolean shouldCreateEmptyCells = false;
private String sheetName = null;
public SheetBuilder(Workbook workbook, Object[][] cells) {
this.workbook = workbook;
@ -43,43 +44,60 @@ public class SheetBuilder {
}
/**
* @return true if null objects should be trated as empty cells
* false otherwise
* Returns {@code true} if null array elements should be treated as empty
* cells.
*
* @return {@code true} if null objects should be treated as empty cells
* and {@code false} otherwise
*/
public boolean getCreateEmptyCells() {
return shouldCreateEmptyCells;
}
/**
* @param shouldCreateEmptyCells true if null array elements should be
* trated as empty cells
* @return this
* Specifies if null array elements should be treated as empty cells.
*
* @param shouldCreateEmptyCells {@code true} if null array elements should be
* treated as empty cells
* @return {@code this}
*/
public SheetBuilder setCreateEmptyCells(boolean shouldCreateEmptyCells) {
this.shouldCreateEmptyCells = shouldCreateEmptyCells;
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
* 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:
*
* <p/>
* <ul>
* <li>Numbers become numeric cells.</li>
* <li><code>java.util.Date</code> or <code>java.util.Calendar</code>
* instances become date cells.</li>
* <li>String with leading '=' char become formulas (leading '='
* trancated).</li>
* will be truncated).</li>
* <li>Other objects become strings via <code>Object.toString()</code>
* method.</li>
* method call.</li>
* </ul>
*
* @return newly created sheet
*/
public Sheet build() {
Sheet sheet = workbook.createSheet();
Sheet sheet = (sheetName == null) ? workbook.createSheet() : workbook.createSheet(sheetName);
Row currentRow = null;
Cell currentCell = null;
@ -100,10 +118,11 @@ public class SheetBuilder {
/**
* 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) {
private void setCellValue(Cell cell, Object value) {
if (value == null || cell == null) {
return;
} else if (value instanceof Number) {

View File

@ -17,12 +17,10 @@
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.
@ -35,9 +33,6 @@ public class CellWalk {
private CellRangeAddress range;
private boolean traverseEmptyCells;
public CellWalk(DataMarker dm) {
this(dm.getSheet(), dm.getRange());
}
public CellWalk(Sheet sheet, CellRangeAddress range) {
this.sheet = sheet;
@ -48,6 +43,7 @@ public class CellWalk {
/**
* Should we call handler on empty (blank) cells. Default is
* false.
*
* @return true if handler should be called on empty (blank)
* cells, false otherwise.
*/
@ -57,6 +53,7 @@ public class CellWalk {
/**
* Sets the traverseEmptyCells property.
*
* @param traverseEmptyCells new property value
*/
public void setTraverseEmptyCells(boolean traverseEmptyCells) {
@ -65,6 +62,7 @@ public class CellWalk {
/**
* Traverse cell range from top left to bottom right cell.
*
* @param handler handler to call on each appropriate cell
*/
public void traverse(CellHandler handler) {
@ -107,6 +105,7 @@ public class CellWalk {
/**
* Inner class to hold walk context.
*
* @author Roman Kashitsyn
*/
private class SimpleCellWalkContext implements CellWalkContext {

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

View File

@ -18,10 +18,12 @@
package org.apache.poi.xssf.usermodel.charts;
import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.util.Beta;
/**
* @author Roman Kashitsyn
*/
@Beta
public class XSSFChartDataFactory implements ChartDataFactory {
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() {
return new XSSFScatterChartData();

View File

@ -17,6 +17,7 @@
package org.apache.poi.xssf.usermodel.charts;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal;
import org.apache.poi.ss.usermodel.charts.ChartLegend;
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
* @author Roman Kashitsyn
*/
@Beta
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;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal;
import org.apache.poi.ss.usermodel.charts.ManualLayout;
import org.apache.poi.ss.usermodel.charts.LayoutMode;
@ -35,6 +36,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget;
* Represents a SpreadsheetML manual layout.
* @author Roman Kashitsyn
*/
@Beta
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;
import java.util.List;
import java.util.ArrayList;
import org.apache.poi.ss.usermodel.Chart;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.DataMarker;
import org.apache.poi.ss.usermodel.charts.ChartAxis;
import org.apache.poi.ss.usermodel.charts.ChartDataSource;
import org.apache.poi.ss.usermodel.charts.ScatterChartData;
import org.apache.poi.ss.usermodel.charts.ScatterChartSerie;
import org.apache.poi.ss.usermodel.charts.ChartDataFactory;
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.util.Beta;
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
*/
@Beta
public class XSSFScatterChartData implements ScatterChartData {
/**
@ -68,48 +53,33 @@ public class XSSFScatterChartData implements ScatterChartData {
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;
private ChartDataSource<?> xs;
private ChartDataSource<? extends Number> ys;
protected Serie(int id, int order) {
protected Serie(int id, int order,
ChartDataSource<?> xs,
ChartDataSource<? extends Number> ys) {
super();
this.id = id;
this.order = order;
this.useCache = true;
}
public void setXValues(DataMarker marker) {
xMarker = marker;
}
public void setYValues(DataMarker marker) {
yMarker = marker;
this.xs = xs;
this.ys = ys;
}
/**
* @param useCache if true, cached results will be added on plot
* Returns data source used for X axis values.
* @return data source used for X axis values
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
public ChartDataSource<?> getXValues() {
return xs;
}
/**
* Returns last calculated number cache for X axis.
* @return last calculated number cache for X axis.
* Returns data source used for Y axis values.
* @return data source used for Y axis values
*/
XSSFNumberCache getLastCaculatedXCache() {
return lastCaclulatedXCache;
}
/**
* Returns last calculated number cache for Y axis.
* @return last calculated number cache for Y axis.
*/
XSSFNumberCache getLastCalculatedYCache() {
return lastCalculatedYCache;
public ChartDataSource<? extends Number> getYValues() {
return ys;
}
protected void addToChart(CTScatterChart ctScatterChart) {
@ -117,31 +87,21 @@ public class XSSFScatterChartData implements ScatterChartData {
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());
XSSFChartUtil.buildAxDataSource(xVal, xs);
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);
}
XSSFChartUtil.buildNumDataSource(yVal, ys);
}
}
public ScatterChartSerie addSerie(DataMarker xMarker, DataMarker yMarker) {
public ScatterChartSerie addSerie(ChartDataSource<?> xs,
ChartDataSource<? extends Number> ys) {
if (!ys.isNumeric()) {
throw new IllegalArgumentException("Y axis data source must be numeric.");
}
int numOfSeries = series.size();
Serie newSerie = new Serie(numOfSeries, numOfSeries);
newSerie.setXValues(xMarker);
newSerie.setYValues(yMarker);
Serie newSerie = new Serie(numOfSeries, numOfSeries, xs, ys);
series.add(newSerie);
return newSerie;
}

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.AxisCrosses;
import org.apache.poi.util.Beta;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTAxPos;
@ -39,6 +40,7 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.STTickLblPos;
*
* @author Roman Kashitsyn
*/
@Beta
public class XSSFValueAxis extends XSSFChartAxis implements ValueAxis {
private CTValAx ctValAx;

View File

@ -40,7 +40,7 @@ public final class TestXSSFChartSheet extends TestCase {
XSSFChartSheet sheet = (XSSFChartSheet)wb.getSheetAt(2);
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
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() {
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,19 +21,19 @@ import junit.framework.TestCase;
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;
/**
* Tests for XSSFScatterChartData.
*
* @author Roman Kashitsyn
*/
public final class TestXSSFScatterChartData extends TestCase {
private static Object[][] plotData = new Object[][] {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
private static final Object[][] plotData = {
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
};
@ -50,13 +50,14 @@ public final class TestXSSFScatterChartData extends TestCase {
ScatterChartData scatterChartData =
chart.getChartDataFactory().createScatterChartData();
DataMarker xMarker = new DataMarker(sheet, CellRangeAddress.valueOf("A1:A10"));
DataMarker yMarker = new DataMarker(sheet, CellRangeAddress.valueOf("B1:B10"));
ScatterChartSerie serie = scatterChartData.addSerie(xMarker, yMarker);
ChartDataSource<String> xs = DataSources.fromStringCellRange(sheet, CellRangeAddress.valueOf("A1:J1"));
ChartDataSource<Number> ys = DataSources.fromNumericCellRange(sheet, CellRangeAddress.valueOf("A2:J2"));
ScatterChartSerie serie = scatterChartData.addSerie(xs, ys);
assertNotNull(serie);
assertEquals(1, scatterChartData.getSeries().size());
assertTrue(scatterChartData.getSeries().contains(serie));
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,6 +29,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Tests SheetBuilder.
*
* @see org.apache.poi.ss.util.SheetBuilder
*/
public final class TestSheetBuilder extends TestCase {
@ -74,4 +75,10 @@ public final class TestSheetBuilder extends TestCase {
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());
}
}