package org.apache.poi.hssf.usermodel;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.record.chart.*;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg;
+import org.apache.poi.hssf.record.formula.AreaPtgBase;
import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.chart.LinkedDataRecord;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressBase;
/**
* Has methods for construction of a chart object.
* @author Glen Stampoultzis (glens at apache.org)
*/
public final class HSSFChart {
+ private HSSFSheet sheet;
private ChartRecord chartRecord;
private LegendRecord legendRecord;
private SeriesTextRecord chartTitleText;
private List<ValueRangeRecord> valueRanges = new ArrayList<ValueRangeRecord>();
+ private HSSFChartType type = HSSFChartType.Unknown;
+
private List<HSSFSeries> series = new ArrayList<HSSFSeries>();
- private HSSFChart(ChartRecord chartRecord) {
+ public enum HSSFChartType {
+ Area {
+ @Override
+ public short getSid() {
+ return 0x101A;
+ }
+ },
+ Bar {
+ @Override
+ public short getSid() {
+ return 0x1017;
+ }
+ },
+ Line {
+ @Override
+ public short getSid() {
+ return 0x1018;
+ }
+ },
+ Pie {
+ @Override
+ public short getSid() {
+ return 0x1019;
+ }
+ },
+ Scatter {
+ @Override
+ public short getSid() {
+ return 0x101B;
+ }
+ },
+ Unknown {
+ @Override
+ public short getSid() {
+ return 0;
+ }
+ };
+
+ public abstract short getSid();
+ }
+
+ private HSSFChart(HSSFSheet sheet, ChartRecord chartRecord) {
this.chartRecord = chartRecord;
+ this.sheet = sheet;
}
/**
for(RecordBase r : records) {
if(r instanceof ChartRecord) {
- lastChart = new HSSFChart((ChartRecord)r);
+ lastSeries = null;
+
+ lastChart = new HSSFChart(sheet,(ChartRecord)r);
charts.add(lastChart);
- }
- if(r instanceof LegendRecord) {
+ } else if(r instanceof LegendRecord) {
lastChart.legendRecord = (LegendRecord)r;
- }
- if(r instanceof SeriesRecord) {
+ } else if(r instanceof SeriesRecord) {
HSSFSeries series = lastChart.new HSSFSeries( (SeriesRecord)r );
lastChart.series.add(series);
lastSeries = series;
- }
- if(r instanceof ChartTitleFormatRecord) {
+ } else if(r instanceof ChartTitleFormatRecord) {
lastChart.chartTitleFormat =
(ChartTitleFormatRecord)r;
- }
- if(r instanceof SeriesTextRecord) {
+ } else if(r instanceof SeriesTextRecord) {
// Applies to a series, unless we've seen
// a legend already
SeriesTextRecord str = (SeriesTextRecord)r;
} else {
lastChart.chartTitleText = str;
}
- }
- if(r instanceof LinkedDataRecord) {
- LinkedDataRecord data = (LinkedDataRecord)r;
- lastSeries.insertData( data );
- }
- if(r instanceof ValueRangeRecord){
+ } else if (r instanceof LinkedDataRecord) {
+ LinkedDataRecord linkedDataRecord = (LinkedDataRecord) r;
+ if (lastSeries != null) {
+ lastSeries.insertData(linkedDataRecord);
+ }
+ } else if(r instanceof ValueRangeRecord){
lastChart.valueRanges.add((ValueRangeRecord)r);
+ } else if (r instanceof Record) {
+ if (lastChart != null)
+ {
+ Record record = (Record) r;
+ for (HSSFChartType type : HSSFChartType.values()) {
+ if (type == HSSFChartType.Unknown)
+ {
+ continue;
+ }
+ if (record.getSid() == type.getSid()) {
+ lastChart.type = type ;
+ break;
+ }
+ }
+ }
}
}
private LinkedDataRecord dataValues;
private LinkedDataRecord dataCategoryLabels;
private LinkedDataRecord dataSecondaryCategoryLabels;
- private int dataReaded = 0;
/* package */ HSSFSeries(SeriesRecord series) {
this.series = series;
}
- public void insertData(LinkedDataRecord data){
- switch(dataReaded){
+ /* package */ void insertData(LinkedDataRecord data){
+ switch(data.getLinkType()){
case 0: dataName = data;
break;
case 1: dataValues = data;
case 3: dataSecondaryCategoryLabels = data;
break;
}
- dataReaded++;
+ }
+
+ /* package */ void setSeriesTitleText(SeriesTextRecord seriesTitleText)
+ {
+ this.seriesTitleText = seriesTitleText;
}
public short getNumValues() {
public SeriesRecord getSeries() {
return series;
}
+
+ private CellRangeAddressBase getCellRange(LinkedDataRecord linkedDataRecord) {
+ if (linkedDataRecord == null)
+ {
+ return null ;
+ }
+
+ int firstRow = 0;
+ int lastRow = 0;
+ int firstCol = 0;
+ int lastCol = 0;
+
+ for (Ptg ptg : linkedDataRecord.getFormulaOfLink()) {
+ if (ptg instanceof AreaPtgBase) {
+ AreaPtgBase areaPtg = (AreaPtgBase) ptg;
+
+ firstRow = areaPtg.getFirstRow();
+ lastRow = areaPtg.getLastRow();
+
+ firstCol = areaPtg.getFirstColumn();
+ lastCol = areaPtg.getLastColumn();
+ }
+ }
+
+ return new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+ }
+
+ public CellRangeAddressBase getValuesCellRange() {
+ return getCellRange(dataValues);
+ }
+
+ public CellRangeAddressBase getCategoryLabelsCellRange() {
+ return getCellRange(dataCategoryLabels);
+ }
+
+ private Integer setVerticalCellRange(LinkedDataRecord linkedDataRecord,
+ CellRangeAddressBase range) {
+ if (linkedDataRecord == null)
+ {
+ return null;
+ }
+
+ List<Ptg> ptgList = new ArrayList<Ptg>();
+
+ int rowCount = (range.getLastRow() - range.getFirstRow()) + 1;
+ int colCount = (range.getLastColumn() - range.getFirstColumn()) + 1;
+
+ for (Ptg ptg : linkedDataRecord.getFormulaOfLink()) {
+ if (ptg instanceof AreaPtgBase) {
+ AreaPtgBase areaPtg = (AreaPtgBase) ptg;
+
+ areaPtg.setFirstRow(range.getFirstRow());
+ areaPtg.setLastRow(range.getLastRow());
+
+ areaPtg.setFirstColumn(range.getFirstColumn());
+ areaPtg.setLastColumn(range.getLastColumn());
+ ptgList.add(areaPtg);
+ }
+ }
+
+ linkedDataRecord.setFormulaOfLink(ptgList.toArray(new Ptg[ptgList.size()]));
+
+ return rowCount * colCount;
+ }
+
+ public void setValuesCellRange(CellRangeAddressBase range) {
+ Integer count = setVerticalCellRange(dataValues, range);
+ if (count == null)
+ {
+ return;
+ }
+
+ series.setNumValues((short)(int)count);
+ }
+
+ public void setCategoryLabelsCellRange(CellRangeAddressBase range) {
+ Integer count = setVerticalCellRange(dataCategoryLabels, range);
+ if (count == null)
+ {
+ return;
+ }
+
+ series.setNumCategories((short)(int)count);
+ }
+ }
+
+ public HSSFSeries createSeries() throws Exception {
+ ArrayList<RecordBase> seriesTemplate = new ArrayList<RecordBase>();
+ boolean seriesTemplateFilled = false;
+
+ int idx = 0;
+ int deep = 0;
+ int chartRecordIdx = -1;
+ int chartDeep = -1;
+ int lastSeriesDeep = -1;
+ int endSeriesRecordIdx = -1;
+ int seriesIdx = 0;
+ final List<RecordBase> records = sheet.getSheet().getRecords();
+
+ /* store first series as template and find last series index */
+ for(final RecordBase record : records) {
+
+ idx++;
+
+ if (record instanceof BeginRecord) {
+ deep++;
+ } else if (record instanceof EndRecord) {
+ deep--;
+
+ if (lastSeriesDeep == deep) {
+ lastSeriesDeep = -1;
+ endSeriesRecordIdx = idx;
+ if (!seriesTemplateFilled) {
+ seriesTemplate.add(record);
+ seriesTemplateFilled = true;
+ }
+ }
+
+ if (chartDeep == deep) {
+ break;
+ }
+ }
+
+ if (record instanceof ChartRecord) {
+ if (record == chartRecord) {
+ chartRecordIdx = idx;
+ chartDeep = deep;
+ }
+ } else if (record instanceof SeriesRecord) {
+ if (chartRecordIdx != -1) {
+ seriesIdx++;
+ lastSeriesDeep = deep;
+ }
+ }
+
+ if (lastSeriesDeep != -1 && !seriesTemplateFilled) {
+ seriesTemplate.add(record) ;
+ }
+ }
+
+ /* check if a series was found */
+ if (endSeriesRecordIdx == -1) {
+ return null;
+ }
+
+ /* next index in the records list where the new series can be inserted */
+ idx = endSeriesRecordIdx + 1;
+
+ HSSFSeries newSeries = null;
+
+ /* duplicate record of the template series */
+ ArrayList<RecordBase> clonedRecords = new ArrayList<RecordBase>();
+ for(final RecordBase record : seriesTemplate) {
+
+ Record newRecord = null;
+
+ if (record instanceof BeginRecord) {
+ newRecord = new BeginRecord();
+ } else if (record instanceof EndRecord) {
+ newRecord = new EndRecord();
+ } else if (record instanceof SeriesRecord) {
+ SeriesRecord seriesRecord = (SeriesRecord) ((SeriesRecord)record).clone();
+ newSeries = new HSSFSeries(seriesRecord);
+ newRecord = seriesRecord;
+ } else if (record instanceof LinkedDataRecord) {
+ LinkedDataRecord linkedDataRecord = (LinkedDataRecord) ((LinkedDataRecord)record).clone();
+ if (newSeries != null) {
+ newSeries.insertData(linkedDataRecord);
+ }
+ newRecord = linkedDataRecord;
+ } else if (record instanceof DataFormatRecord) {
+ DataFormatRecord dataFormatRecord = (DataFormatRecord) ((DataFormatRecord)record).clone();
+
+ dataFormatRecord.setSeriesIndex((short)seriesIdx) ;
+ dataFormatRecord.setSeriesNumber((short)seriesIdx) ;
+
+ newRecord = dataFormatRecord;
+ } else if (record instanceof SeriesTextRecord) {
+ SeriesTextRecord seriesTextRecord = (SeriesTextRecord) ((SeriesTextRecord)record).clone();
+ if (newSeries != null) {
+ newSeries.setSeriesTitleText(seriesTextRecord);
+ }
+ newRecord = seriesTextRecord;
+ } else if (record instanceof Record) {
+ newRecord = (Record) ((Record)record).clone();
+ }
+
+ if (newRecord != null)
+ {
+ clonedRecords.add(newRecord);
+ }
+ }
+
+ /* check if a user model series object was created */
+ if (newSeries == null)
+ {
+ return null;
+ }
+
+ /* transfer series to record list */
+ for(final RecordBase record : clonedRecords) {
+ records.add(idx++, record);
+ }
+
+ return newSeries;
+ }
+
+ public boolean removeSeries(HSSFSeries series) {
+ int idx = 0;
+ int deep = 0;
+ int chartDeep = -1;
+ int lastSeriesDeep = -1;
+ int seriesIdx = -1;
+ boolean removeSeries = false;
+ boolean chartEntered = false;
+ boolean result = false;
+ final List<RecordBase> records = sheet.getSheet().getRecords();
+
+ /* store first series as template and find last series index */
+ Iterator<RecordBase> iter = records.iterator();
+ while (iter.hasNext()) {
+ RecordBase record = iter.next();
+ idx++;
+
+ if (record instanceof BeginRecord) {
+ deep++;
+ } else if (record instanceof EndRecord) {
+ deep--;
+
+ if (lastSeriesDeep == deep) {
+ lastSeriesDeep = -1;
+
+ if (removeSeries) {
+ removeSeries = false;
+ result = true;
+ iter.remove();
+ }
+ }
+
+ if (chartDeep == deep) {
+ break;
+ }
+ }
+
+ if (record instanceof ChartRecord) {
+ if (record == chartRecord) {
+ chartDeep = deep;
+ chartEntered = true;
+ }
+ } else if (record instanceof SeriesRecord) {
+ if (chartEntered) {
+ if (series.series == record) {
+ lastSeriesDeep = deep;
+ removeSeries = true;
+ } else {
+ seriesIdx++;
+ }
+ }
+ } else if (record instanceof DataFormatRecord) {
+ if (chartEntered && !removeSeries) {
+ DataFormatRecord dataFormatRecord = (DataFormatRecord) record;
+ dataFormatRecord.setSeriesIndex((short) seriesIdx);
+ dataFormatRecord.setSeriesNumber((short) seriesIdx);
+ }
+ }
+
+ if (removeSeries) {
+ iter.remove();
+ }
+ }
+
+ return result;
+ }
+
+ public HSSFChartType getType() {
+ return type;
}
}
import junit.framework.TestCase;
+import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.chart.SeriesRecord;
+import org.apache.poi.hssf.usermodel.HSSFChart.HSSFSeries;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressBase;
/**
* Tests for {@link HSSFChart}
assertEquals("Base Numbers", charts[0].getSeries()[1].getSeriesTitle());
assertEquals("Sheet 3 Chart with Title", charts[0].getChartTitle());
}
+
+ public void testExistingSheet3() throws Exception {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("49581.xls");
+
+ HSSFSheet sheet = wb.getSheetAt( 2 ) ;
+ HSSFChart[] charts = HSSFChart.getSheetCharts( sheet ) ;
+ assertEquals(1, charts.length);
+
+ for ( HSSFChart chart : charts ) {
+ for ( HSSFSeries series : chart.getSeries() ) {
+ chart.removeSeries( series ) ;
+ }
+ }
+
+ // Save and re-check
+ wb = HSSFITestDataProvider.instance.writeOutAndReadBack(wb);
+ sheet = wb.getSheetAt( 2 ) ;
+ assertEquals(1, HSSFChart.getSheetCharts(sheet).length);
+
+ HSSFChart c = HSSFChart.getSheetCharts(sheet)[0];
+ assertEquals(0, c.getSeries().length);
+ }
+
+ public void testExistingSheet2() throws Exception {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("49581.xls");
+ HSSFSheet sheet = wb.getSheetAt( 1 ) ;
+ HSSFChart[] charts = HSSFChart.getSheetCharts( sheet ) ;
+
+ assertEquals(1, charts.length);
+ for ( HSSFChart chart : charts ) {
+ HSSFSeries series ;
+
+ // Starts with one
+ assertEquals(1, chart.getSeries().length);
+
+ // Add two more
+ series = chart.createSeries() ;
+ series.setCategoryLabelsCellRange( new CellRangeAddress( 3, 4, 0, 0 ) ) ;
+ series.setValuesCellRange( new CellRangeAddress( 3, 4, 1, 1 ) ) ;
+
+ series = chart.createSeries() ;
+ series.setCategoryLabelsCellRange( new CellRangeAddress( 6, 7, 0, 0 ) ) ;
+ series.setValuesCellRange( new CellRangeAddress( 6, 7, 1, 1 ) ) ;
+ }
+
+ // Save and re-check
+ wb = HSSFITestDataProvider.instance.writeOutAndReadBack(wb);
+ sheet = wb.getSheetAt( 1 ) ;
+ assertEquals(1, HSSFChart.getSheetCharts(sheet).length);
+
+ HSSFChart c = HSSFChart.getSheetCharts(sheet)[0];
+ assertEquals(3, c.getSeries().length);
+ }
+
+ public void testExistingSheet1() throws Exception {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("49581.xls");
+ HSSFSheet sheet = wb.getSheetAt( 0 ) ;
+ HSSFChart[] charts = HSSFChart.getSheetCharts( sheet ) ;
+
+ for ( HSSFChart chart : charts ) {
+ //System.out.println( chart.getType() ) ;
+ HSSFSeries[] seriesArray = chart.getSeries() ;
+ //System.out.println( "seriesArray.length=" + seriesArray.length ) ;
+ for ( HSSFSeries series : seriesArray )
+ {
+ //System.out.println( "serie.getNumValues()=" + series.getNumValues() ) ;
+ CellRangeAddressBase range ;
+
+ range = series.getValuesCellRange() ;
+ //System.out.println( range.toString() ) ;
+ range.setLastRow( range.getLastRow() + 1 ) ;
+ series.setValuesCellRange( range ) ;
+
+ range = series.getCategoryLabelsCellRange() ;
+ //System.out.println( range.toString() ) ;
+ range.setLastRow( range.getLastRow() + 1 ) ;
+ series.setCategoryLabelsCellRange( range ) ;
+ }
+
+ for ( int id = 0 ; id < 2 ; id++ )
+ {
+ HSSFSeries newSeries = chart.createSeries() ;
+ newSeries.setValuesCellRange( new CellRangeAddress( 1 + id, 4, 3, 3 ) ) ;
+ String oldSeriesTitle = newSeries.getSeriesTitle() ;
+ if ( oldSeriesTitle != null )
+ {
+ //System.out.println( "old series title: " + oldSeriesTitle ) ;
+ newSeries.setSeriesTitle( "new series" ) ;
+ }
+ }
+ }
+
+ HSSFChart chart = charts[ 2 ] ;
+ chart.removeSeries( chart.getSeries()[ 0 ] ) ;
+ }
}