/* ====================================================================
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.
==================================================================== */
namespace HH.WMS.Utils.NPOI.HSSF.Model
{
using System;
using System.Collections;
using System.Collections.Generic;
using HH.WMS.Utils.NPOI.HSSF.Record;
using HH.WMS.Utils.NPOI.HSSF.Record.Aggregates;
using HH.WMS.Utils.NPOI.SS.Formula;
using HH.WMS.Utils.NPOI.SS.Util;
///
/// Low level model implementation of a Sheet (one workbook Contains many sheets)
/// This file Contains the low level binary records starting at the sheets BOF and
/// ending with the sheets EOF. Use HSSFSheet for a high level representation.
///
/// The structures of the highlevel API use references to this to perform most of their
/// operations. Its probably Unwise to use these low level structures directly Unless you
/// really know what you're doing. I recommend you Read the Microsoft Excel 97 Developer's
/// Kit (Microsoft Press) and the documentation at http://sc.openoffice.org/excelfileformat.pdf
/// before even attempting to use this.
///
///
/// @author Andrew C. Oliver (acoliver at apache dot org)
/// @author Glen Stampoultzis (glens at apache.org)
/// @author Shawn Laubach (slaubach at apache dot org) Gridlines, Headers, Footers, PrintSetup, and Setting Default Column Styles
/// @author Jason Height (jheight at chariot dot net dot au) Clone support. DBCell and Index Record writing support
/// @author Brian Sanders (kestrel at burdell dot org) Active Cell support
/// @author Jean-Pierre Paris (jean-pierre.paris at m4x dot org) (Just a little)
///
[Serializable]
public class InternalSheet
{
//private static POILogger log = POILogFactory.GetLogger(typeof(Sheet));
int preoffset = 0; // offset of the sheet in a new file
protected int dimsloc = -1; // TODO - Is it legal for dims record to be missing?
[NonSerialized]
protected DimensionsRecord dims;
[NonSerialized]
protected DefaultColWidthRecord defaultcolwidth = new DefaultColWidthRecord();
[NonSerialized]
protected DefaultRowHeightRecord defaultrowheight = new DefaultRowHeightRecord();
[NonSerialized]
protected GridsetRecord gridset = null;
[NonSerialized]
protected PrintSetupRecord printSetup = null;
[NonSerialized]
protected HeaderRecord header = null;
[NonSerialized]
protected FooterRecord footer = null;
[NonSerialized]
protected PrintGridlinesRecord printGridlines = null;
[NonSerialized]
protected WindowTwoRecord windowTwo = null;
[NonSerialized]
protected MergeCellsRecord merged = null;
/** java object always present, but if empty no BIFF records are written */
[NonSerialized]
private MergedCellsTable _mergedCellsTable;
[NonSerialized]
protected RowRecordsAggregate _rowsAggregate;
[NonSerialized]
private PageSettingsBlock _psBlock;
protected Margin[] margins = null;
//protected IList mergedRecords = new ArrayList();
[NonSerialized]
protected SelectionRecord selection = null;
//protected ColumnInfoRecordsAggregate columns = null;
//protected ValueRecordsAggregate cells = null;
/*package*/
[NonSerialized]
internal ColumnInfoRecordsAggregate _columnInfos;
/** the DimensionsRecord is always present */
[NonSerialized]
private DimensionsRecord _dimensions;
[NonSerialized]
private DataValidityTable _dataValidityTable = null;
//private IEnumerator valueRecEnumerator = null;
private IEnumerator rowRecEnumerator = null;
protected int eofLoc = 0;
[NonSerialized]
private GutsRecord _gutsRecord;
[NonSerialized]
protected PageBreakRecord rowBreaks = null;
[NonSerialized]
protected PageBreakRecord colBreaks = null;
[NonSerialized]
protected ConditionalFormattingTable condFormatting;
[NonSerialized]
protected SheetExtRecord sheetext;
protected List records = null;
/** Add an UncalcedRecord if not true indicating formulas have not been calculated */
protected bool _isUncalced = false;
//public static byte PANE_LOWER_RIGHT = (byte)0;
//public static byte PANE_UPPER_RIGHT = (byte)1;
//public static byte PANE_LOWER_LEFT = (byte)2;
//public static byte PANE_UPPER_LEFT = (byte)3;
///
/// Clones the low level records of this sheet and returns the new sheet instance.
/// This method is implemented by Adding methods for deep cloning to all records that
/// can be Added to a sheet. The Record object does not implement Cloneable.
/// When Adding a new record, implement a public Clone method if and only if the record
/// belongs to a sheet.
///
///
public InternalSheet CloneSheet()
{
List clonedRecords = new List(this.records.Count);
for (int i = 0; i < this.records.Count; i++)
{
RecordBase rb = (RecordBase)this.records[i];
if (rb is RecordAggregate)
{
((RecordAggregate)rb).VisitContainedRecords(new RecordCloner(clonedRecords));
continue;
}
Record rec = (Record)((Record)rb).Clone();
clonedRecords.Add(rec);
}
return CreateSheet(new RecordStream(clonedRecords, 0));
}
///
/// get the NEXT value record (from LOC). The first record that is a value record
/// (starting at LOC) will be returned.
/// This method is "loc" sensitive. Meaning you need to set LOC to where you
/// want it to start searching. If you don't know do this: setLoc(getDimsLoc).
/// When adding several rows you can just start at the last one by leaving loc
/// at what this sets it to. For this method, set loc to dimsloc to start with,
/// subsequent calls will return values in (physical) sequence or NULL when you get to the end.
///
/// the next value record or NULL if there are no more
//
public CellValueRecordInterface[] GetValueRecords()
{
return _rowsAggregate.GetValueRecords();
}
public WindowTwoRecord WindowTwo
{
get { return windowTwo; }
}
///
/// Creates the sheet.
///
/// The stream.
///
public static InternalSheet CreateSheet(RecordStream rs)
{
return new InternalSheet(rs);
}
private class RecordCloner : RecordVisitor
{
private IList _destList;
public RecordCloner(IList destList)
{
_destList = destList;
}
public void VisitRecord(Record r)
{
_destList.Add(r.Clone());
}
}
///
/// Initializes a new instance of the class.
///
/// The stream.
private InternalSheet(RecordStream rs)
{
_mergedCellsTable = new MergedCellsTable();
RowRecordsAggregate rra = null;
records = new List(128);
// TODO - take chart streams off into separate java objects
//int bofEofNestingLevel = 0; // nesting level can only get to 2 (when charts are present)
int dimsloc = -1;
if (rs.PeekNextSid() != BOFRecord.sid)
{
throw new Exception("BOF record expected");
}
BOFRecord bof = (BOFRecord)rs.GetNext();
if (bof.Type != BOFRecord.TYPE_WORKSHEET)
{
// TODO - fix junit tests throw new RuntimeException("Bad BOF record type");
}
records.Add(bof);
while (rs.HasNext())
{
int recSid = rs.PeekNextSid();
if (recSid == CFHeaderRecord.sid)
{
condFormatting = new ConditionalFormattingTable(rs);
records.Add(condFormatting);
continue;
}
if (recSid == ColumnInfoRecord.sid)
{
_columnInfos = new ColumnInfoRecordsAggregate(rs);
records.Add(_columnInfos);
continue;
}
if (recSid == DVALRecord.sid)
{
_dataValidityTable = new DataValidityTable(rs);
records.Add(_dataValidityTable);
continue;
}
if (RecordOrderer.IsRowBlockRecord(recSid))
{
//only Add the aggregate once
if (rra != null)
{
throw new InvalidOperationException("row/cell records found in the wrong place");
}
RowBlocksReader rbr = new RowBlocksReader(rs);
_mergedCellsTable.AddRecords(rbr.LooseMergedCells);
rra = new RowRecordsAggregate(rbr.PlainRecordStream, rbr.SharedFormulaManager);
records.Add(rra); //only Add the aggregate once
continue;
}
if (CustomViewSettingsRecordAggregate.IsBeginRecord(recSid))
{
// This happens three times in test sample file "29982.xls"
// Also several times in bugzilla samples 46840-23373 and 46840-23374
records.Add(new CustomViewSettingsRecordAggregate(rs));
continue;
}
if (PageSettingsBlock.IsComponentRecord(recSid))
{
if (_psBlock == null)
{
// first PSB record encountered - read all of them:
_psBlock = new PageSettingsBlock(rs);
records.Add(_psBlock);
}
else
{
// one or more PSB records found after some intervening non-PSB records
_psBlock.AddLateRecords(rs);
}
// YK: in some cases records can be moved to the preceding
// CustomViewSettingsRecordAggregate blocks
_psBlock.PositionRecords(records);
continue;
}
if (WorksheetProtectionBlock.IsComponentRecord(recSid))
{
_protectionBlock.AddRecords(rs);
continue;
}
if (recSid == MergeCellsRecord.sid)
{
// when the MergedCellsTable is found in the right place, we expect those records to be contiguous
_mergedCellsTable.Read(rs);
continue;
}
if (recSid == BOFRecord.sid)
{
ChartSubstreamRecordAggregate chartAgg = new ChartSubstreamRecordAggregate(rs);
//if (false)
//{
// TODO - would like to keep the chart aggregate packed, but one unit test needs attention
// records.Add(chartAgg);
//}
//else
//{
SpillAggregate(chartAgg, records);
//}
continue;
}
Record rec = rs.GetNext();
if (recSid == IndexRecord.sid)
{
// ignore INDEX record because it is only needed by Excel,
// and POI always re-calculates its contents
continue;
}
if (recSid == UncalcedRecord.sid)
{
// don't Add UncalcedRecord to the list
_isUncalced = true; // this flag is enough
continue;
}
if (recSid == FeatRecord.sid ||
recSid == FeatHdrRecord.sid)
{
records.Add(rec);
continue;
}
if (recSid == EOFRecord.sid)
{
records.Add(rec);
break;
}
if (recSid == DimensionsRecord.sid)
{
// Make a columns aggregate if one hasn't Ready been created.
if (_columnInfos == null)
{
_columnInfos = new ColumnInfoRecordsAggregate();
records.Add(_columnInfos);
}
_dimensions = (DimensionsRecord)rec;
dimsloc = records.Count;
}
else if (recSid == DefaultColWidthRecord.sid)
{
defaultcolwidth = (DefaultColWidthRecord)rec;
}
else if (recSid == DefaultRowHeightRecord.sid)
{
defaultrowheight = (DefaultRowHeightRecord)rec;
}
else if (recSid == PrintGridlinesRecord.sid)
{
printGridlines = (PrintGridlinesRecord)rec;
}
else if (recSid == GridsetRecord.sid)
{
gridset = (GridsetRecord)rec;
}
else if (recSid == SelectionRecord.sid)
{
selection = (SelectionRecord)rec;
}
else if (recSid == WindowTwoRecord.sid)
{
windowTwo = (WindowTwoRecord)rec;
}
else if (recSid == SheetExtRecord.sid)
{
sheetext = (SheetExtRecord)rec;
}
else if (recSid == GutsRecord.sid)
{
_gutsRecord = (GutsRecord)rec;
}
records.Add(rec);
}
if (windowTwo == null)
{
throw new InvalidOperationException("WINDOW2 was not found");
}
if (_dimensions == null)
{
// Excel seems to always write the DIMENSION record, but tolerates when it is not present
// in all cases Excel (2007) adds the missing DIMENSION record
if (rra == null)
{
// bug 46206 alludes to files which skip the DIMENSION record
// when there are no row/cell records.
// Not clear which application wrote these files.
rra = new RowRecordsAggregate();
}
else
{
//log.log(POILogger.WARN, "DIMENSION record not found even though row/cells present");
// Not sure if any tools write files like this, but Excel reads them OK
}
dimsloc = FindFirstRecordLocBySid(WindowTwoRecord.sid);
_dimensions = rra.CreateDimensions();
records.Insert(dimsloc, _dimensions);
}
if (rra == null)
{
rra = new RowRecordsAggregate();
records.Insert(dimsloc + 1, rra);
}
_rowsAggregate = rra;
// put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */
RecordOrderer.AddNewSheetRecord(records, _mergedCellsTable);
RecordOrderer.AddNewSheetRecord(records, _protectionBlock);
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
}
private class RecordVisitor1:RecordVisitor
{
List _records;
public RecordVisitor1(List recs)
{
_records=recs;
}
public void VisitRecord(Record r) {
_records.Add(r);
}
}
private static void SpillAggregate(RecordAggregate ra, List recs) {
ra.VisitContainedRecords(new RecordVisitor1(recs));
}
///
/// Creates a sheet with all the usual records minus values and the "index"
/// record (not required). Sets the location pointer to where the first value
/// records should go. Use this to Create a sheet from "scratch".
///
/// Sheet object with all values Set to defaults
public static InternalSheet CreateSheet()
{
return new InternalSheet();
}
private InternalSheet()
{
_mergedCellsTable = new MergedCellsTable();
records = new List(32);
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "Sheet Createsheet from scratch called");
records.Add(CreateBOF());
records.Add(CreateCalcMode());
records.Add(CreateCalcCount());
records.Add(CreateRefMode());
records.Add(CreateIteration());
records.Add(CreateDelta());
records.Add(CreateSaveRecalc());
records.Add(CreatePrintHeaders());
printGridlines = CreatePrintGridlines();
records.Add(printGridlines);
gridset = CreateGridset();
records.Add(gridset);
_gutsRecord = CreateGuts();
records.Add(_gutsRecord);
defaultrowheight = CreateDefaultRowHeight();
records.Add(defaultrowheight);
records.Add(CreateWSBool());
// 'Page Settings Block'
_psBlock = new PageSettingsBlock();
records.Add(_psBlock);
// 'Worksheet Protection Block' (after 'Page Settings Block' and before DEFCOLWIDTH)
records.Add(_protectionBlock);
defaultcolwidth = CreateDefaultColWidth();
records.Add(defaultcolwidth);
ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate();
records.Add(columns);
_columnInfos = columns;
_dimensions = CreateDimensions();
records.Add(_dimensions);
_rowsAggregate = new RowRecordsAggregate();
records.Add(_rowsAggregate);
// 'Sheet View Settings'
records.Add(windowTwo = CreateWindowTwo());
selection = CreateSelection();
records.Add(selection);
records.Add(_mergedCellsTable); // MCT comes after 'Sheet View Settings'
sheetext = new SheetExtRecord();
records.Add(sheetext);
records.Add(EOFRecord.instance);
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "Sheet Createsheet from scratch exit");
}
///
/// Adds the merged region.
///
/// the row index From
/// The column index From.
/// The row index To
/// The column To.
///
public int AddMergedRegion(int rowFrom, int colFrom, int rowTo, int colTo)
{
// Validate input
if (rowTo < rowFrom)
{
throw new ArgumentException("The 'to' row (" + rowTo
+ ") must not be less than the 'from' row (" + rowFrom + ")");
}
if (colTo < colFrom)
{
throw new ArgumentException("The 'to' col (" + colTo
+ ") must not be less than the 'from' col (" + colFrom + ")");
}
MergedCellsTable mrt = MergedRecords;
mrt.AddArea(rowFrom, colFrom, rowTo, colTo);
return mrt.NumberOfMergedRegions - 1;
}
///
/// Removes the merged region.
///
/// The index.
public void RemoveMergedRegion(int index)
{
//safety checks
MergedCellsTable mrt = MergedRecords;
if (index >= mrt.NumberOfMergedRegions)
{
return;
}
mrt.Remove(index);
}
///
/// Gets the column infos.
///
/// The column infos.
public ColumnInfoRecordsAggregate ColumnInfos
{
get { return _columnInfos; }
}
internal MergedCellsTable MergedRecords
{
get
{
// always present
return _mergedCellsTable;
}
}
///
/// Gets the merged region at.
///
/// The index.
///
public CellRangeAddress GetMergedRegionAt(int index)
{
//safety checks
MergedCellsTable mrt = MergedRecords;
if (index >= mrt.NumberOfMergedRegions)
{
return null;
}
return mrt.Get(index);
}
///
/// Gets the number of merged regions.
///
/// The number merged regions.
public int NumMergedRegions
{
get
{
return MergedRecords.NumberOfMergedRegions;
}
}
/////
///// Find correct position to Add new CF record
/////
/////
//private int FindConditionalFormattingPosition()
//{
// // This is default.
// // If the algorithm does not Find the right position,
// // this one will be used (this is a position before EOF record)
// int index = records.Count - 2;
// for (int i = index; i >= 0; i--)
// {
// Record rec = (Record)records[i];
// short sid = rec.Sid;
// // CFRecordsAggregate records already exist, just Add to the end
// if (rec is CFRecordsAggregate) { return i + 1; }
// if (sid == (short)0x00ef) { return i + 1; }// PHONETICPR
// if (sid == (short)0x015f) { return i + 1; }// LABELRANGES
// if (sid == MergeCellsRecord.sid) { return i + 1; }
// if (sid == (short)0x0099) { return i + 1; }// STANDARDWIDTH
// if (sid == SelectionRecord.sid) { return i + 1; }
// if (sid == PaneRecord.sid) { return i + 1; }
// if (sid == SCLRecord.sid) { return i + 1; }
// if (sid == WindowTwoRecord.sid) { return i + 1; }
// }
// return index;
//}
///
/// Gets the number of conditional formattings.
///
/// The number of conditional formattings.
public int NumConditionalFormattings
{
get
{
return condFormatting.Count;
}
}
///
/// Per an earlier reported bug in working with Andy Khan's excel Read library. This
/// Sets the values in the sheet's DimensionsRecord object to be correct. Excel doesn't
/// really care, but we want to play nice with other libraries.
///
/// The first row.
/// The first column.
/// The last row.
/// The last column.
public void SetDimensions(int firstrow, short firstcol, int lastrow,
short lastcol)
{
//if (log.Check(POILogger.DEBUG))
//{
// log.Log(POILogger.DEBUG, "Sheet.SetDimensions");
// log.Log(POILogger.DEBUG,
// (new StringBuilder("firstrow")).Append(firstrow)
// .Append("firstcol").Append(firstcol).Append("lastrow")
// .Append(lastrow).Append("lastcol").Append(lastcol)
// .ToString());
//}
dims.FirstCol = firstcol;
dims.FirstRow = firstrow;
dims.LastCol = lastcol;
dims.LastRow = lastrow;
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "Sheet.SetDimensions exiting");
}
///
/// Gets or Sets the preoffset when using DBCELL records (currently Unused) - this Is
/// the position of this sheet within the whole file.
///
/// the offset of the sheet's BOF within the file.
public int PreOffset
{
get
{
return preoffset;
}
set
{
this.preoffset = value;
}
}
///
/// Create a row record. (does not Add it to the records contained in this sheet)
///
/// row number
/// RowRecord Created for the passed in row number
public RowRecord CreateRow(int row)
{
return RowRecordsAggregate.CreateRow(row);
}
///
/// Create a LABELSST Record (does not Add it to the records contained in this sheet)
///
/// the row the LabelSST Is a member of
/// the column the LabelSST defines
/// the index of the string within the SST (use workbook AddSSTString method)
/// LabelSSTRecord newly Created containing your SST Index, row,col.
public LabelSSTRecord CreateLabelSST(int row, short col, int index)
{
// log.LogFormatted(POILogger.DEBUG, "Create labelsst row,col,index %,%,%",
// new int[]
//{
// row, col, index
//});
LabelSSTRecord rec = new LabelSSTRecord();
rec.Row = (row);
rec.Column = (col);
rec.SSTIndex = (index);
rec.XFIndex = ((short)0x0f);
return rec;
}
///
/// Create a NUMBER Record (does not Add it to the records contained in this sheet)
///
/// the row the NumberRecord is a member of
/// the column the NumberRecord defines
/// value for the number record
/// NumberRecord for that row, col containing that value as Added to the sheet
public NumberRecord CreateNumber(int row, short col, double value)
{
//log.LogFormatted(POILogger.DEBUG, "Create number row,col,value %,%,%",
// new double[]
//{
// row, col, value
//});
NumberRecord rec = new NumberRecord();
rec.Row = row;
rec.Column = col;
rec.Value = value;
rec.XFIndex = (short)0x0f;
return rec;
}
///
/// Create a BLANK record (does not Add it to the records contained in this sheet)
///
/// the row the BlankRecord is a member of
/// the column the BlankRecord is a member of
///
public BlankRecord CreateBlank(int row, short col)
{
//log.LogFormatted(POILogger.DEBUG, "Create blank row,col %,%", new int[]
//{
// row, col
//});
BlankRecord rec = new BlankRecord();
rec.Row = row;
rec.Column = col;
rec.XFIndex = (short)0x0f;
return rec;
}
///
/// Adds a value record to the sheet's contained binary records
/// (i.e. LabelSSTRecord or NumberRecord).
/// This method is "loc" sensitive. Meaning you need to Set LOC to where you
/// want it to start searching. If you don't know do this: SetLoc(GetDimsLoc).
/// When Adding several rows you can just start at the last one by leaving loc
/// at what this Sets it to.
///
/// the row to Add the cell value to
/// the cell value record itself.
public void AddValueRecord(int row, CellValueRecordInterface col)
{
//if (log.Check(POILogger.DEBUG))
//{
// log.Log(POILogger.DEBUG, "Add value record row" + row);
//}
DimensionsRecord d = _dimensions;
if (col.Column > d.LastCol)
{
d.LastCol = ((short)(col.Column + 1));
}
if (col.Column < d.FirstCol)
{
d.FirstCol = (col.Column);
}
_rowsAggregate.InsertCell(col);
}
///
/// Remove a value record from the records array.
/// This method is not loc sensitive, it Resets loc to = dimsloc so no worries.
///
/// the row of the value record you wish to Remove
/// a record supporting the CellValueRecordInterface.
public void RemoveValueRecord(int row, CellValueRecordInterface col)
{
//log.LogFormatted(POILogger.DEBUG, "Remove value record row,dimsloc %,%",
// new int[] { row, dimsloc });
_rowsAggregate.RemoveCell(col);
}
///
/// Replace a value record from the records array.
/// This method is not loc sensitive, it Resets loc to = dimsloc so no worries.
///
/// a record supporting the CellValueRecordInterface. this will Replace
/// the cell value with the same row and column. If there Isn't one, one will
/// be Added.
public void ReplaceValueRecord(CellValueRecordInterface newval)
{
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "ReplaceValueRecord ");
//The ValueRecordsAggregate use a tree map Underneath.
//The tree Map uses the CellValueRecordInterface as both the
//key and the value, if we dont do a Remove, then
//the previous instance of the key is retained, effectively using
//double the memory
_rowsAggregate.RemoveCell(newval);
_rowsAggregate.InsertCell(newval);
}
///
/// Adds a row record to the sheet
/// This method is "loc" sensitive. Meaning you need to Set LOC to where you
/// want it to start searching. If you don't know do this: SetLoc(GetDimsLoc).
/// When Adding several rows you can just start at the last one by leaving loc
/// at what this Sets it to.
///
/// the row record to be Added
public void AddRow(RowRecord row)
{
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "AddRow ");
DimensionsRecord d = _dimensions;
if (row.RowNumber >= d.LastRow)
{
d.LastRow = (row.RowNumber + 1);
}
if (row.RowNumber < d.FirstRow)
{
d.FirstRow = (row.RowNumber);
}
//IndexRecord index = null;
//If the row exists Remove it, so that any cells attached to the row are Removed
RowRecord existingRow = _rowsAggregate.GetRow(row.RowNumber);
if (existingRow != null)
{
_rowsAggregate.RemoveRow(existingRow);
}
_rowsAggregate.InsertRow(row);
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "exit AddRow");
}
///
/// Removes a row record
/// This method is not loc sensitive, it Resets loc to = dimsloc so no worries.
///
/// the row record to Remove
public void RemoveRow(RowRecord row)
{
_rowsAggregate.RemoveRow(row);
}
///
/// Get the NEXT RowRecord (from LOC). The first record that is a Row record
/// (starting at LOC) will be returned.
/// This method is "loc" sensitive. Meaning you need to Set LOC to where you
/// want it to start searching. If you don't know do this: SetLoc(GetDimsLoc).
/// When Adding several rows you can just start at the last one by leaving loc
/// at what this Sets it to. For this method, Set loc to dimsloc to start with.
/// subsequent calls will return rows in (physical) sequence or NULL when you Get to the end.
///
/// RowRecord representing the next row record or NULL if there are no more
public RowRecord NextRow
{
get
{
if (rowRecEnumerator == null)
{
rowRecEnumerator = _rowsAggregate.GetEnumerator();
}
if (!rowRecEnumerator.MoveNext())
{
return null;
}
return (RowRecord)rowRecEnumerator.Current;
}
}
///
/// Get the NEXT (from LOC) RowRecord where rownumber matches the given rownum.
/// The first record that is a Row record (starting at LOC) that has the
/// same rownum as the given rownum will be returned.
/// This method is "loc" sensitive. Meaning you need to Set LOC to where you
/// want it to start searching. If you don't know do this: SetLoc(GetDimsLoc).
/// When Adding several rows you can just start at the last one by leaving loc
/// at what this Sets it to. For this method, Set loc to dimsloc to start with.
/// subsequent calls will return rows in (physical) sequence or NULL when you Get to the end.
///
/// which row to return (careful with LOC)
/// RowRecord representing the next row record or NULL if there are no more
public RowRecord GetRow(int rownum)
{
return _rowsAggregate.GetRow(rownum);
}
///
/// Gets the page settings.
///
///
public PageSettingsBlock PageSettings
{
get
{
if (_psBlock == null)
{
_psBlock = new PageSettingsBlock();
RecordOrderer.AddNewSheetRecord(records, _psBlock);
}
return _psBlock;
}
}
///
/// Creates the BOF record
///
/// record containing a BOFRecord
public static Record CreateBOF()
{
BOFRecord retval = new BOFRecord();
retval.Version = ((short)0x600);
retval.Type = ((short)0x010);
retval.Build = ((short)0x0dbb);
retval.BuildYear = ((short)1996);
retval.HistoryBitMask = (0xc1);
retval.RequiredVersion = (0x6);
return retval;
}
///
/// Creates the Index record - not currently used
///
/// record containing a IndexRecord
protected Record CreateIndex()
{
IndexRecord retval = new IndexRecord();
retval.FirstRow = (0); // must be Set explicitly
retval.LastRowAdd1 = (0);
return retval;
}
///
/// Creates the CalcMode record and Sets it to 1 (automatic formula caculation)
///
/// record containing a CalcModeRecord
protected Record CreateCalcMode()
{
CalcModeRecord retval = new CalcModeRecord();
retval.SetCalcMode((short)1);
return retval;
}
///
/// Creates the CalcCount record and Sets it to 0x64 (default number of iterations)
///
/// record containing a CalcCountRecord
protected Record CreateCalcCount()
{
CalcCountRecord retval = new CalcCountRecord();
retval.Iterations = ((short)0x64); // default 64 iterations
return retval;
}
///
/// Creates the RefMode record and Sets it to A1 Mode (default reference mode)
///
/// record containing a RefModeRecord
protected Record CreateRefMode()
{
RefModeRecord retval = new RefModeRecord();
retval.Mode = RefModeRecord.USE_A1_MODE;
return retval;
}
///
/// Creates the Iteration record and Sets it to false (don't iteratively calculate formulas)
///
/// record containing a IterationRecord
protected Record CreateIteration()
{
return new IterationRecord(false);
}
///
/// Creates the Delta record and Sets it to 0.0010 (default accuracy)
///
/// record containing a DeltaRecord
protected Record CreateDelta()
{
return new DeltaRecord(DeltaRecord.DEFAULT_VALUE);
}
///
/// Creates the SaveRecalc record and Sets it to true (recalculate before saving)
///
/// record containing a SaveRecalcRecord
protected Record CreateSaveRecalc()
{
SaveRecalcRecord retval = new SaveRecalcRecord();
retval.Recalc = (true);
return retval;
}
///
/// Creates the PrintHeaders record and Sets it to false (we don't Create headers yet so why print them)
///
/// record containing a PrintHeadersRecord
protected Record CreatePrintHeaders()
{
PrintHeadersRecord retval = new PrintHeadersRecord();
retval.PrintHeaders = false;
return retval;
}
///
/// Creates the PrintGridlines record and Sets it to false (that makes for ugly sheets). As far as I can
/// tell this does the same thing as the GridsetRecord
///
/// record containing a PrintGridlinesRecord
protected PrintGridlinesRecord CreatePrintGridlines()
{
PrintGridlinesRecord retval = new PrintGridlinesRecord();
retval.PrintGridlines = false;
return retval;
}
///
/// Creates the GridSet record and Sets it to true (user has mucked with the gridlines)
///
/// record containing a GridsetRecord
protected GridsetRecord CreateGridset()
{
GridsetRecord retval = new GridsetRecord();
retval.Gridset = (true);
return retval;
}
///
/// Creates the Guts record and Sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
///
/// record containing a GutsRecordRecord
protected GutsRecord CreateGuts()
{
GutsRecord retval = new GutsRecord();
retval.LeftRowGutter = ((short)0);
retval.TopColGutter = ((short)0);
retval.RowLevelMax = ((short)0);
retval.ColLevelMax = ((short)0);
return retval;
}
///
/// Creates the DefaultRowHeight Record and Sets its options to 0 and rowheight to 0xff
///
///
///
/// record containing a DefaultRowHeightRecord
protected DefaultRowHeightRecord CreateDefaultRowHeight()
{
DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
retval.OptionFlags = ((short)0);
retval.RowHeight = ((short)0xff);
return retval;
}
/**
* Creates the WSBoolRecord and Sets its values to defaults
* @see org.apache.poi.hssf.record.WSBoolRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a WSBoolRecord
*/
protected Record CreateWSBool()
{
WSBoolRecord retval = new WSBoolRecord();
retval.WSBool1 = ((byte)0x4);
retval.WSBool2 = ((byte)0x1);
return retval;
}
/**
* Creates the HCenter Record and Sets it to false (don't horizontally center)
* @see org.apache.poi.hssf.record.HCenterRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a HCenterRecord
*/
protected Record CreateHCenter()
{
HCenterRecord retval = new HCenterRecord();
retval.HCenter = (false);
return retval;
}
/**
* Creates the VCenter Record and Sets it to false (don't horizontally center)
* @see org.apache.poi.hssf.record.VCenterRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a VCenterRecord
*/
protected Record CreateVCenter()
{
VCenterRecord retval = new VCenterRecord();
retval.VCenter = (false);
return retval;
}
/**
* Creates the PrintSetup Record and Sets it to defaults and marks it invalid
* @see org.apache.poi.hssf.record.PrintSetupRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a PrintSetupRecord
*/
protected Record CreatePrintSetup()
{
PrintSetupRecord retval = new PrintSetupRecord();
retval.PaperSize = ((short)1);
retval.Scale = ((short)100);
retval.PageStart = ((short)1);
retval.FitWidth = ((short)1);
retval.FitHeight = ((short)1);
retval.Options = ((short)2);
retval.HResolution = ((short)300);
retval.VResolution = ((short)300);
retval.HeaderMargin = (0.5);
retval.FooterMargin = (0.5);
retval.Copies = ((short)0);
return retval;
}
/**
* Creates the DefaultColWidth Record and Sets it to 8
* @see org.apache.poi.hssf.record.DefaultColWidthRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a DefaultColWidthRecord
*/
protected DefaultColWidthRecord CreateDefaultColWidth()
{
DefaultColWidthRecord retval = new DefaultColWidthRecord();
retval.ColWidth = (short)DefaultColWidthRecord.DEFAULT_COLUMN_WIDTH;
return retval;
}
/**
* Get the default column width for the sheet (if the columns do not define their own width)
* @return default column width
*/
public int DefaultColumnWidth
{
get { return defaultcolwidth.ColWidth; }
set { defaultcolwidth.ColWidth = (short)value; }
}
/**
* Get the default row height for the sheet (if the rows do not define their own height)
* @return default row height
*/
public short DefaultRowHeight
{
get { return defaultrowheight.RowHeight; }
set { defaultrowheight.RowHeight = (value); }
}
/**
* Get the width of a given column in Units of 1/256th of a Char width
* @param column index
* @see org.apache.poi.hssf.record.DefaultColWidthRecord
* @see org.apache.poi.hssf.record.ColumnInfoRecord
* @see #SetColumnWidth(short,short)
* @return column width in Units of 1/256th of a Char width
*/
public int GetColumnWidth(int columnIndex)
{
ColumnInfoRecord ci = _columnInfos.FindColumnInfo(columnIndex);
if (ci != null)
{
return ci.ColumnWidth;
}
//default column width is measured in characters
//multiply
return (256 * defaultcolwidth.ColWidth);
}
/**
* Get the index to the ExtendedFormatRecord "associated" with
* the column at specified 0-based index. (In this case, an
* ExtendedFormatRecord index is actually associated with a
* ColumnInfoRecord which spans 1 or more columns)
*
* Returns the index to the default ExtendedFormatRecord (0xF)
* if no ColumnInfoRecord exists that includes the column
* index specified.
* @param column
* @return index of ExtendedFormatRecord associated with
* ColumnInfoRecord that includes the column index or the
* index of the default ExtendedFormatRecord (0xF)
*/
public short GetXFIndexForColAt(short columnIndex)
{
ColumnInfoRecord ci = _columnInfos.FindColumnInfo(columnIndex);
if (ci != null)
{
return (short)ci.XFIndex;
}
return 0xF;
}
/**
* Set the width for a given column in 1/256th of a Char width Units
* @param column - the column number
* @param width (in Units of 1/256th of a Char width)
*/
public void SetColumnWidth(int column, int width)
{
if (width > 255 * 256)
throw new ArgumentException("The maximum column width for an individual cell is 255 characters.");
SetColumn(column, null, width, null, null, null);
}
/**
* Get the hidden property for a given column.
* @param column index
* @see org.apache.poi.hssf.record.DefaultColWidthRecord
* @see org.apache.poi.hssf.record.ColumnInfoRecord
* @see #SetColumnHidden(short,bool)
* @return whether the column is hidden or not.
*/
public bool IsColumnHidden(int columnIndex)
{
ColumnInfoRecord cir = _columnInfos.FindColumnInfo(columnIndex);
if (cir == null)
{
return false;
}
return cir.IsHidden;
}
/**
* Get the hidden property for a given column.
* @param column - the column number
* @param hidden - whether the column is hidden or not
*/
public void SetColumnHidden(int column, bool hidden)
{
SetColumn(column, null, null, null, hidden, null);
}
public void SetDefaultColumnStyle(int column, int styleIndex)
{
SetColumn(column, (short)styleIndex, null, null, null, null);
}
public void SetColumn(int column, int width, int level, bool hidden, bool collapsed)
{
_columnInfos.SetColumn(column, 0, width, level, hidden, collapsed);
}
public void SetColumn(int column, short? xfStyle, int? width, int? level, bool? hidden, bool? collapsed)
{
_columnInfos.SetColumn(column, xfStyle, width, level, hidden, collapsed);
}
private GutsRecord GetGutsRecord()
{
if (_gutsRecord == null)
{
GutsRecord result = CreateGuts();
RecordOrderer.AddNewSheetRecord(records, result);
_gutsRecord = result;
}
return _gutsRecord;
}
/**
* Creates an outline Group for the specified columns.
* @param fromColumn Group from this column (inclusive)
* @param toColumn Group to this column (inclusive)
* @param indent if true the Group will be indented by one level,
* if false indenting will be Removed by one level.
*/
public void GroupColumnRange(int fromColumn, int toColumn, bool indent)
{
// Set the level for each column
_columnInfos.GroupColumnRange(fromColumn, toColumn, indent);
// Determine the maximum overall level
int maxLevel = _columnInfos.MaxOutlineLevel;
GutsRecord guts = GetGutsRecord();
guts.ColLevelMax = (short)(maxLevel + 1);
if (maxLevel == 0)
guts.TopColGutter = ((short)0);
else
guts.TopColGutter = ((short)(29 + (12 * (maxLevel - 1))));
}
/**
* Creates the Dimensions Record and Sets it to bogus values (you should Set this yourself
* or let the high level API do it for you)
* @see org.apache.poi.hssf.record.DimensionsRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a DimensionsRecord
*/
private static DimensionsRecord CreateDimensions()
{
DimensionsRecord retval = new DimensionsRecord();
retval.FirstCol = (short)0;
retval.LastRow = 1; // one more than it Is
retval.FirstRow = 0;
retval.LastCol = (short)1; // one more than it Is
return retval;
}
/**
* Creates the WindowTwo Record and Sets it to:
* options = 0x6b6
* toprow = 0
* leftcol = 0
* headercolor = 0x40
* pagebreakzoom = 0x0
* normalzoom = 0x0
* @see org.apache.poi.hssf.record.WindowTwoRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a WindowTwoRecord
*/
private static WindowTwoRecord CreateWindowTwo()
{
WindowTwoRecord retval = new WindowTwoRecord();
retval.Options = ((short)0x6b6);
retval.TopRow = ((short)0);
retval.LeftCol = ((short)0);
retval.HeaderColor = (0x40);
retval.PageBreakZoom = ((short)0);
retval.NormalZoom = ((short)0);
return retval;
}
///
/// Creates the Selection record and Sets it to nothing selected
///
/// record containing a SelectionRecord
private static SelectionRecord CreateSelection()
{
return new SelectionRecord(0, 0);
}
///
/// Gets or sets the top row.
///
/// The top row.
public short TopRow
{
get
{
return (windowTwo == null) ? (short)0 : windowTwo.TopRow;
}
set
{
if (windowTwo != null)
{
windowTwo.TopRow = (value);
}
}
}
///
/// Gets or sets the left col.
///
/// The left col.
public short LeftCol
{
get
{
return (windowTwo == null) ? (short)0 : windowTwo.LeftCol;
}
set
{
if (windowTwo != null)
{
windowTwo.LeftCol = (value);
}
}
}
///
/// Sets the active cell.
///
/// The row.
/// The column.
public void SetActiveCell(int row, int column)
{
this.SetActiveCellRange(row, row, column, column);
}
///
/// Sets the active cell range.
///
/// The firstrow.
/// The lastrow.
/// The firstcolumn.
/// The lastcolumn.
public void SetActiveCellRange(int firstRow, int lastRow, int firstColumn, int lastColumn)
{
List cellranges = new List();
cellranges.Add(new CellRangeAddress8Bit(firstRow, lastRow, firstColumn, lastColumn));
this.SetActiveCellRange(cellranges, 0, firstRow, firstColumn);
}
///
/// Sets the active cell range.
///
/// The cellranges.
/// The index of the active range.
/// The active row in the active range
/// The active column in the active range
public void SetActiveCellRange(List cellranges, int activeRange, int activeRow, int activeColumn)
{
this.selection.ActiveCellCol = activeColumn;
this.selection.ActiveCellRow = activeRow;
this.selection.ActiveCellRef = activeRange;
this.selection.CellReferences = cellranges.ToArray();
}
///
/// Returns the active row
///
/// the active row index
/// @see org.apache.poi.hssf.record.SelectionRecord
public int ActiveCellRow
{
get
{
if (selection == null)
{
return 0;
}
return selection.ActiveCellRow;
}
}
///
/// Gets or sets the active cell col.
///
/// the active column index
/// @see org.apache.poi.hssf.record.SelectionRecord
public int ActiveCellCol
{
get
{
if (selection == null)
{
return 0;
}
return selection.ActiveCellCol;
}
}
///
/// Creates the EOF record
///
/// record containing a EOFRecord
protected Record CreateEOF()
{
return new EOFRecord();
}
public IList Records
{
get { return records; }
}
///
/// Gets the gridset record for this sheet.
///
/// The gridset record.
public GridsetRecord GridsetRecord
{
get
{
return gridset;
}
}
private GutsRecord GutsRecord
{
get
{
if (_gutsRecord == null)
{
GutsRecord result = CreateGuts();
RecordOrderer.AddNewSheetRecord(records, result);
_gutsRecord = result;
}
return _gutsRecord;
}
}
///
/// Returns the first occurance of a record matching a particular sid.
///
/// The sid.
///
public Record FindFirstRecordBySid(short sid)
{
int ix = FindFirstRecordLocBySid(sid);
if (ix < 0)
{
return null;
}
return (Record)records[ix];
}
///
/// Sets the SCL record or Creates it in the correct place if it does not
/// already exist.
///
/// The record to set.
public void SetSCLRecord(SCLRecord sclRecord)
{
int oldRecordLoc = FindFirstRecordLocBySid(SCLRecord.sid);
if (oldRecordLoc == -1)
{
// Insert it after the window record
int windowRecordLoc = FindFirstRecordLocBySid(WindowTwoRecord.sid);
records.Insert(windowRecordLoc + 1, sclRecord);
}
else
{
records[oldRecordLoc] = sclRecord;
}
}
/**
* Finds the first occurance of a record matching a particular sid and
* returns it's position.
* @param sid the sid to search for
* @return the record position of the matching record or -1 if no match
* is made.
*/
public int FindFirstRecordLocBySid(short sid)
{
int max = records.Count;
for (int i = 0; i < max; i++)
{
Object rb = records[i];
if (!(rb is Record))
{
continue;
}
Record record = (Record)rb;
if (record.Sid == sid)
{
return i;
}
}
return -1;
}
///
/// Gets or sets the header.
///
/// the HeaderRecord.
public HeaderRecord Header
{
get { return header; }
set { header = value; }
}
///
/// Gets or sets a value indicating whether this instance is auto tab color.
///
///
/// true if this instance is auto tab color; otherwise, false.
///
public bool IsAutoTabColor
{
get { return sheetext.IsAutoColor; }
set
{
sheetext.IsAutoColor = value;
}
}
public short TabColorIndex
{
get { return sheetext.TabColorIndex; }
set
{
if ((value <= 0x08 || value >= 0x3F) && value != 0x7F)
{
throw new ArgumentException("invalid color index");
}
sheetext.TabColorIndex = value;
}
}
public WindowTwoRecord GetWindowTwo()
{
return windowTwo;
}
///
/// Gets or sets the footer.
///
/// FooterRecord for the sheet.
public FooterRecord Footer
{
get { return footer; }
set { footer = value; }
}
/**
* Returns the PrintSetupRecord.
* @return PrintSetupRecord for the sheet.
*/
public PrintSetupRecord PrintSetup
{
get { return printSetup; }
set { printSetup = value; }
}
/**
* @return true if gridlines are printed
*/
public bool IsGridsPrinted
{
get
{
if (gridset == null)
{
gridset = CreateGridset();
//Insert the newlycreated Gridset record at the end of the record (just before the EOF)
int loc = FindFirstRecordLocBySid(EOFRecord.sid);
records.Insert(loc, gridset);
}
return !gridset.Gridset;
}
set
{
gridset.Gridset = !value;
}
}
/**
* Returns the PrintGridlinesRecord.
* @return PrintGridlinesRecord for the sheet.
*/
public PrintGridlinesRecord PrintGridlines
{
get { return printGridlines; }
set { printGridlines = value; }
}
/**
* Sets whether the sheet is selected
* @param sel True to select the sheet, false otherwise.
*/
public void SetSelected(bool sel)
{
windowTwo.IsSelected = (sel);
}
/**
* Creates a split (freezepane). Any existing freezepane or split pane Is overwritten.
* @param colSplit Horizonatal position of split.
* @param rowSplit Vertical position of split.
* @param topRow Top row visible in bottom pane
* @param leftmostColumn Left column visible in right pane.
*/
public void CreateFreezePane(int colSplit, int rowSplit, int topRow, int leftmostColumn)
{
int paneLoc = FindFirstRecordLocBySid(PaneRecord.sid);
if (paneLoc != -1)
records.RemoveAt(paneLoc);
// If both colSplit and rowSplit are zero then the existing freeze pane is removed
if (colSplit == 0 && rowSplit == 0)
{
windowTwo.FreezePanes = (false);
windowTwo.FreezePanesNoSplit = (false);
SelectionRecord sel = (SelectionRecord)FindFirstRecordBySid(SelectionRecord.sid);
sel.Pane = (PaneInformation.PANE_UPPER_LEFT);
return;
}
int loc = FindFirstRecordLocBySid(WindowTwoRecord.sid);
PaneRecord pane = new PaneRecord();
pane.X = ((short)colSplit);
pane.Y = ((short)rowSplit);
pane.TopRow = ((short)topRow);
pane.LeftColumn = ((short)leftmostColumn);
if (rowSplit == 0)
{
pane.TopRow = ((short)0);
pane.ActivePane = ((short)1);
}
else if (colSplit == 0)
{
pane.LeftColumn = ((short)0);
pane.ActivePane = ((short)2);
}
else
{
pane.ActivePane = ((short)0);
}
records.Insert(loc + 1, pane);
windowTwo.FreezePanes = (true);
windowTwo.FreezePanesNoSplit = (true);
SelectionRecord sel1 = (SelectionRecord)FindFirstRecordBySid(SelectionRecord.sid);
sel1.Pane = ((byte)pane.ActivePane);
}
/**
* Creates a split pane. Any existing freezepane or split pane is overwritten.
* @param xSplitPos Horizonatal position of split (in 1/20th of a point).
* @param ySplitPos Vertical position of split (in 1/20th of a point).
* @param topRow Top row visible in bottom pane
* @param leftmostColumn Left column visible in right pane.
* @param activePane Active pane. One of: PANE_LOWER_RIGHT,
* PANE_UPPER_RIGHT, PANE_LOWER_LEFT, PANE_UPPER_LEFT
* @see #PANE_LOWER_LEFT
* @see #PANE_LOWER_RIGHT
* @see #PANE_UPPER_LEFT
* @see #PANE_UPPER_RIGHT
*/
public void CreateSplitPane(int xSplitPos, int ySplitPos, int topRow, int leftmostColumn, HH.WMS.Utils.NPOI.SS.UserModel.PanePosition activePane)
{
int paneLoc = FindFirstRecordLocBySid(PaneRecord.sid);
if (paneLoc != -1)
records.RemoveAt(paneLoc);
int loc = FindFirstRecordLocBySid(WindowTwoRecord.sid);
PaneRecord r = new PaneRecord();
r.X = ((short)xSplitPos);
r.Y = ((short)ySplitPos);
r.TopRow = ((short)topRow);
r.LeftColumn = ((short)leftmostColumn);
r.ActivePane = ((short)activePane);
records.Insert(loc + 1, r);
windowTwo.FreezePanes = (false);
windowTwo.FreezePanesNoSplit = (false);
SelectionRecord sel = (SelectionRecord)FindFirstRecordBySid(SelectionRecord.sid);
sel.Pane = (byte)HH.WMS.Utils.NPOI.SS.UserModel.PanePosition.LOWER_RIGHT;
}
/**
* Returns the information regarding the currently configured pane (split or freeze).
* @return null if no pane configured, or the pane information.
*/
public PaneInformation PaneInformation
{
get
{
PaneRecord rec = (PaneRecord)FindFirstRecordBySid(PaneRecord.sid);
if (rec == null)
return null;
return new HH.WMS.Utils.NPOI.SS.Util.PaneInformation(rec.X, rec.Y, rec.TopRow,
rec.LeftColumn, (byte)rec.ActivePane, windowTwo.FreezePanes);
}
}
public SelectionRecord Selection
{
get
{
return selection;
}
set { this.selection = value; }
}
/**
* creates a Password record with password set to 00.
*/
protected static PasswordRecord CreatePassword()
{
//if (log.check(POILogger.DEBUG))
// log.log(POILogger.DEBUG, "create password record with 00 password");
PasswordRecord retval = new PasswordRecord((short)00);
return retval;
}
/**
* creates a Protect record with protect set to false.
*/
protected ProtectRecord CreateProtect()
{
/*if (log.check(POILogger.DEBUG))
{
log.log(POILogger.DEBUG, "create protect record with protection disabled");
}*/
ProtectRecord retval = new ProtectRecord(false);
return retval;
}
/**
* Creates an ObjectProtect record with protect Set to false.
* @see org.apache.poi.hssf.record.ObjectProtectRecord
* @see org.apache.poi.hssf.record.Record
* @return an ObjectProtectRecord
*/
protected ObjectProtectRecord CreateObjectProtect()
{
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "Create protect record with protection disabled");
ObjectProtectRecord retval = new ObjectProtectRecord();
retval.Protect = (false);
return retval;
}
/**
* Creates a ScenarioProtect record with protect Set to false.
* @see org.apache.poi.hssf.record.ScenarioProtectRecord
* @see org.apache.poi.hssf.record.Record
* @return a ScenarioProtectRecord
*/
protected ScenarioProtectRecord CreateScenarioProtect()
{
//if (log.Check(POILogger.DEBUG))
// log.Log(POILogger.DEBUG, "Create protect record with protection disabled");
ScenarioProtectRecord retval = new ScenarioProtectRecord();
retval.Protect = (false);
return retval;
}
//'Worksheet Protection Block'
//Aggregate object is always present, but possibly empty.
[NonSerialized]
private WorksheetProtectionBlock _protectionBlock = new WorksheetProtectionBlock();
public WorksheetProtectionBlock ProtectionBlock
{
get
{
return _protectionBlock;
}
}
/**
* Returns if gridlines are Displayed.
* @return whether gridlines are Displayed
*/
public bool DisplayGridlines
{
get
{
return windowTwo.DisplayGridlines;
}
set { windowTwo.DisplayGridlines = (value); }
}
/**
* Returns if formulas are Displayed.
* @return whether formulas are Displayed
*/
public bool DisplayFormulas
{
get { return windowTwo.DisplayFormulas; }
set { windowTwo.DisplayFormulas = (value); }
}
/**
* Returns if RowColHeadings are Displayed.
* @return whether RowColHeadings are Displayed
*/
public bool DisplayRowColHeadings
{
get
{
return windowTwo.DisplayRowColHeadings;
}
set
{
windowTwo.DisplayRowColHeadings = (value);
}
}
/**
* @return whether an Uncalced record must be Inserted or not at generation
*/
public bool IsUncalced
{
get { return _isUncalced; }
set { this._isUncalced = value; }
}
/**
* Finds the DrawingRecord for our sheet, and
* attaches it to the DrawingManager (which knows about
* the overall DrawingGroup for our workbook).
* If requested, will Create a new DrawRecord
* if none currently exist
* @param drawingManager The DrawingManager2 for our workbook
* @param CreateIfMissing Should one be Created if missing?
*/
public int AggregateDrawingRecords(DrawingManager2 drawingManager, bool CreateIfMissing)
{
int loc = FindFirstRecordLocBySid(DrawingRecord.sid);
bool noDrawingRecordsFound = (loc == -1);
if (noDrawingRecordsFound)
{
if (!CreateIfMissing)
{
// None found, and not allowed to Add in
return -1;
}
EscherAggregate aggregate = new EscherAggregate(drawingManager);
loc = FindFirstRecordLocBySid(EscherAggregate.sid);
if (loc == -1)
{
loc = FindFirstRecordLocBySid(WindowTwoRecord.sid);
}
else
{
Records.RemoveAt(loc);
}
Records.Insert(loc, aggregate);
return loc;
}
else
{
IList records = Records;
EscherAggregate r = EscherAggregate.CreateAggregate(records, loc, drawingManager);
int startloc = loc;
while (loc + 1 < records.Count
&& records[loc] is DrawingRecord
&& (records[loc + 1] is ObjRecord ||
records[loc + 1] is TextObjectRecord)
)
{
loc += 2;
if (records[loc] is NoteRecord) loc ++;
}
int endloc = loc - 1;
for (int i = 0; i < (endloc - startloc + 1); i++)
{
records.RemoveAt(startloc);
}
records.Insert(startloc, r);
return startloc;
}
}
/**
* Perform any work necessary before the sheet is about to be Serialized.
* For instance the escher aggregates size needs to be calculated before
* serialization so that the dgg record (which occurs first) can be written.
*/
public void Preserialize()
{
for (IEnumerator iterator = Records.GetEnumerator(); iterator.MoveNext(); )
{
RecordBase r = (RecordBase)iterator.Current;
if (r is EscherAggregate)
{
int size = r.RecordSize; // Trigger flatterning of user model and corresponding update of dgg record.
}
}
}
/**
* Shifts all the page breaks in the range "count" number of rows/columns
* @param breaks The page record to be Shifted
* @param start Starting "main" value to Shift breaks
* @param stop Ending "main" value to Shift breaks
* @param count number of Units (rows/columns) to Shift by
*/
public void ShiftBreaks(PageBreakRecord breaks, short start, short stop, int count)
{
if (rowBreaks == null)
return;
IEnumerator iterator = breaks.GetBreaksEnumerator();
IList ShiftedBreak = new ArrayList();
while (iterator.MoveNext())
{
PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.Current;
int breakLocation = breakItem.main;
bool inStart = (breakLocation >= start);
bool inEnd = (breakLocation <= stop);
if (inStart && inEnd)
ShiftedBreak.Add(breakItem);
}
iterator = ShiftedBreak.GetEnumerator();
while (iterator.MoveNext())
{
PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.Current;
breaks.RemoveBreak(breakItem.main);
breaks.AddBreak(breakItem.main + count, breakItem.subFrom, breakItem.subTo);
}
}
/**
* Shifts the horizontal page breaks for the indicated count
* @param startingRow
* @param endingRow
* @param count
*/
public void ShiftRowBreaks(int startingRow, int endingRow, int count)
{
ShiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count);
}
/**
* Shifts the vertical page breaks for the indicated count
* @param startingCol
* @param endingCol
* @param count
*/
public void ShiftColumnBreaks(short startingCol, short endingCol, short count)
{
ShiftBreaks(colBreaks, startingCol, endingCol, count);
}
public void SetColumnGroupCollapsed(int columnNumber, bool collapsed)
{
if (collapsed)
{
_columnInfos.CollapseColumn(columnNumber);
}
else
{
_columnInfos.ExpandColumn(columnNumber);
}
}
public RowRecordsAggregate RowsAggregate
{
get
{
return _rowsAggregate;
}
}
/**
* Updates formulas in cells and conditional formats due to moving of cells
* @param externSheetIndex the externSheet index of this sheet
*/
public void UpdateFormulasAfterCellShift(FormulaShifter shifter, int externSheetIndex)
{
RowsAggregate.UpdateFormulasAfterRowShift(shifter, externSheetIndex);
if (condFormatting != null)
{
ConditionalFormattingTable.UpdateFormulasAfterCellShift(shifter, externSheetIndex);
}
// TODO - adjust data validations
}
public ConditionalFormattingTable ConditionalFormattingTable
{
get
{
if (condFormatting == null)
{
condFormatting = new ConditionalFormattingTable();
RecordOrderer.AddNewSheetRecord(records, condFormatting);
}
return condFormatting;
}
}
public void VisitContainedRecords(RecordVisitor rv, int offset)
{
PositionTrackingVisitor ptv = new PositionTrackingVisitor(rv, offset);
bool haveSerializedIndex = false;
int sheetOffset = offset;
for (int k = 0; k < records.Count; k++)
{
RecordBase record = records[k];
if (record is RecordAggregate)
{
RecordAggregate agg = (RecordAggregate)record;
agg.VisitContainedRecords(ptv);
sheetOffset += agg.RecordSize;
}
else
{
if (record is DefaultColWidthRecord)
{
((DefaultColWidthRecord)record).offsetForFilePointer = sheetOffset;
}
ptv.VisitRecord((Record)record);
sheetOffset += record.RecordSize;
}
// If the BOF record was just serialized then add the IndexRecord
if (record is BOFRecord)
{
if (!haveSerializedIndex)
{
haveSerializedIndex = true;
// Add an optional UncalcedRecord. However, we should add
// it in only the once, after the sheet's own BOFRecord.
// If there are diagrams, they have their own BOFRecords,
// and one shouldn't go in after that!
if (_isUncalced)
{
UncalcedRecord rec = new UncalcedRecord();
ptv.VisitRecord(rec);
sheetOffset += rec.RecordSize;
}
//Can there be more than one BOF for a sheet? If not then we can
//remove this guard. So be safe it is left here.
if (_rowsAggregate != null)
{
// find forward distance to first RowRecord
int initRecsSize = GetSizeOfInitialSheetRecords(k);
int currentPos = ptv.Position;
IndexRecord indexRecord = _rowsAggregate.CreateIndexRecord(currentPos, initRecsSize, 0);
ptv.VisitRecord(indexRecord);
sheetOffset += indexRecord.RecordSize;
}
}
}
}
}
/**
* 'initial sheet records' are between INDEX and the 'Row Blocks'
* @param bofRecordIndex index of record after which INDEX record is to be placed
* @return count of bytes from end of INDEX record to first ROW record.
*/
private int GetSizeOfInitialSheetRecords(int bofRecordIndex)
{
int result = 0;
// start just after BOF record (INDEX is not present in this list)
for (int j = bofRecordIndex + 1; j < records.Count; j++)
{
RecordBase tmpRec = records[j];
if (tmpRec is RowRecordsAggregate)
{
break;
}
result += tmpRec.RecordSize;
}
if (_isUncalced)
{
result += UncalcedRecord.StaticRecordSize;
}
return result;
}
public void GroupRowRange(int fromRow, int toRow, bool indent)
{
for (int rowNum = fromRow; rowNum <= toRow; rowNum++)
{
RowRecord row = GetRow(rowNum);
if (row == null)
{
row = CreateRow(rowNum);
AddRow(row);
}
int level = row.OutlineLevel;
if (indent) level++; else level--;
level = Math.Max(0, level);
level = Math.Min(7, level);
row.OutlineLevel = ((short)(level));
}
RecalcRowGutter();
}
private void RecalcRowGutter()
{
int maxLevel = 0;
IEnumerator iterator = _rowsAggregate.GetEnumerator();
while (iterator.MoveNext())
{
RowRecord rowRecord = (RowRecord)iterator.Current;
maxLevel = Math.Max(rowRecord.OutlineLevel, maxLevel);
}
// Grab the guts record, Adding if needed
GutsRecord guts = GetGutsRecord();
if (guts == null)
{
guts = new GutsRecord();
records.Add(guts);
}
// Set the levels onto it
guts.RowLevelMax = ((short)(maxLevel + 1));
guts.LeftRowGutter = (short)(29 + 12 * maxLevel);
}
public DataValidityTable GetOrCreateDataValidityTable()
{
if (_dataValidityTable == null)
{
_dataValidityTable = new DataValidityTable();
RecordOrderer.AddNewSheetRecord(records, _dataValidityTable);
}
return _dataValidityTable;
}
/**
* Get the {@link NoteRecord}s (related to cell comments) for this sheet
* @return never null
, typically empty array
*/
public NoteRecord[] GetNoteRecords()
{
List temp = new List();
for (int i = records.Count - 1; i >= 0; i--)
{
RecordBase rec = records[i];
if (rec is NoteRecord)
{
temp.Add((NoteRecord)rec);
}
}
if (temp.Count < 1)
{
return NoteRecord.EMPTY_ARRAY;
}
NoteRecord[] result = new NoteRecord[temp.Count];
result = temp.ToArray();
return result;
}
}
}