--- src/java/org/apache/poi/hssf/record/chart/ChartEndBlockRecord.java (revision 962890) +++ src/java/org/apache/poi/hssf/record/chart/ChartEndBlockRecord.java (working copy) @@ -35,6 +35,9 @@ private short iObjectKind; private byte[] unused; + public ChartEndBlockRecord() { + } + public ChartEndBlockRecord(RecordInputStream in) { rt = in.readShort(); grbitFrt = in.readShort(); @@ -80,4 +83,16 @@ buffer.append("[/ENDBLOCK]\n"); return buffer.toString(); } + + @Override + public ChartEndBlockRecord clone() { + ChartEndBlockRecord record = new ChartEndBlockRecord(); + + record.rt = rt ; + record.grbitFrt = grbitFrt ; + record.iObjectKind = iObjectKind ; + record.unused = unused.clone() ; + + return record; + } } --- src/java/org/apache/poi/hssf/record/chart/ChartStartBlockRecord.java (revision 962890) +++ src/java/org/apache/poi/hssf/record/chart/ChartStartBlockRecord.java (working copy) @@ -37,6 +37,9 @@ private short iObjectInstance1; private short iObjectInstance2; + public ChartStartBlockRecord() { + } + public ChartStartBlockRecord(RecordInputStream in) { rt = in.readShort(); grbitFrt = in.readShort(); @@ -80,4 +83,18 @@ buffer.append("[/STARTBLOCK]\n"); return buffer.toString(); } + + @Override + public ChartStartBlockRecord clone() { + ChartStartBlockRecord record = new ChartStartBlockRecord(); + + record.rt = rt; + record.grbitFrt = grbitFrt; + record.iObjectKind = iObjectKind; + record.iObjectContext = iObjectContext; + record.iObjectInstance1 = iObjectInstance1; + record.iObjectInstance2 = iObjectInstance2; + + return record; + } } --- src/java/org/apache/poi/hssf/record/RecordFactory.java (revision 962890) +++ src/java/org/apache/poi/hssf/record/RecordFactory.java (working copy) @@ -221,6 +221,7 @@ WriteAccessRecord.class, WriteProtectRecord.class, WSBoolRecord.class, + DataFormatRecord.class, // chart records BeginRecord.class, --- src/java/org/apache/poi/ss/util/CellRangeAddressBase.java (revision 962890) +++ src/java/org/apache/poi/ss/util/CellRangeAddressBase.java (working copy) @@ -27,14 +27,14 @@ * * @author Josh Micich */ -public abstract class CellRangeAddressBase { +public class CellRangeAddressBase { private int _firstRow; private int _firstCol; private int _lastRow; private int _lastCol; - protected CellRangeAddressBase(int firstRow, int lastRow, int firstCol, int lastCol) { + public CellRangeAddressBase(int firstRow, int lastRow, int firstCol, int lastCol) { _firstRow = firstRow; _lastRow = lastRow; _firstCol = firstCol; --- src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java (revision 962890) +++ src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java (working copy) @@ -35,8 +35,9 @@ 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.CellRangeAddressBase; /** * Has methods for construction of a chart object. @@ -44,6 +45,7 @@ * @author Glen Stampoultzis (glens at apache.org) */ public final class HSSFChart { + private HSSFSheet sheet; private ChartRecord chartRecord; private LegendRecord legendRecord; @@ -53,8 +55,9 @@ private List series = new ArrayList(); - private HSSFChart(ChartRecord chartRecord) { + private HSSFChart(HSSFSheet sheet, ChartRecord chartRecord) { this.chartRecord = chartRecord; + this.sheet = sheet; } /** @@ -146,22 +149,20 @@ 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; @@ -173,12 +174,12 @@ } 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); } } @@ -914,7 +915,7 @@ this.series = series; } - public void insertData(LinkedDataRecord data){ + /* package */ void insertData(LinkedDataRecord data){ switch(dataReaded){ case 0: dataName = data; break; @@ -928,6 +929,11 @@ dataReaded++; } + /* package */ void setSeriesTitleText(SeriesTextRecord seriesTitleText) + { + this.seriesTitleText = seriesTitleText; + } + public short getNumValues() { return series.getNumValues(); } @@ -996,5 +1002,208 @@ 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 CellRangeAddressBase(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 ptgList = new ArrayList(); + + 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 seriesTemplate = new ArrayList(); + boolean seriesTemplateFilled = false; + + int idx = 0; + int deep = 0; + int chartRecordIdx = -1; + int chartDeep = -1; + int lastSeriesDeep = -1; + int endSeriesRecordIdx = -1; + int seriesIdx = 0; + List records = sheet.getSheet().getRecords(); + + /* store first series as template and find last series index */ + for(RecordBase record : records) { + + 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 series = null; + + /* duplicate record of the template series */ + ArrayList clonedRecords = new ArrayList(); + for(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(); + series = new HSSFSeries(seriesRecord); + newRecord = seriesRecord; + } else if (record instanceof LinkedDataRecord) { + LinkedDataRecord linkedDataRecord = (LinkedDataRecord) ((LinkedDataRecord)record).clone(); + if (series != null) { + series.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 (series != null) { + series.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 (series == null) + { + return null; + } + + /* transfer series to record list */ + for(RecordBase record : clonedRecords) { + records.add(idx++, record); + } + + return series; + } }