poi/src/java/org/apache/poi/hssf/model/Sheet.java

2615 lines
84 KiB
Java

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache POI" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache POI", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.poi.hssf.model;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.poi.hssf
.record.*; // normally I don't do this, buy we literally mean ALL
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.util.*;
import org.apache.poi.hssf.record
.aggregates.*; // normally I don't do this, buy we literally mean ALL
/**
* 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.
* <P>
* 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.
* <P>
* @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, and PrintSetup
* @author Jason Height (jheight at chariot dot net dot au) Clone support
* @author Brian Sanders (kestrel at burdell dot org) Active Cell support
*
* @see org.apache.poi.hssf.model.Workbook
* @see org.apache.poi.hssf.usermodel.HSSFSheet
* @version 1.0-pre
*/
public class Sheet implements Model
{
public static final short LeftMargin = 0;
public static final short RightMargin = 1;
public static final short TopMargin = 2;
public static final short BottomMargin = 3;
protected ArrayList records = null;
int preoffset = 0; // offset of the sheet in a new file
int loc = 0;
protected boolean containsLabels = false;
protected int dimsloc = 0;
protected DimensionsRecord dims;
protected DefaultColWidthRecord defaultcolwidth = null;
protected DefaultRowHeightRecord defaultrowheight = null;
protected GridsetRecord gridset = null;
protected PrintSetupRecord printSetup = null;
protected HeaderRecord header = null;
protected FooterRecord footer = null;
protected PrintGridlinesRecord printGridlines = null;
protected WindowTwoRecord windowTwo = null;
protected MergeCellsRecord merged = null;
protected Margin margins[] = null;
protected ArrayList mergedRecords = new ArrayList();
protected ArrayList mergedLocs = new ArrayList();
protected int numMergedRegions = 0;
protected SelectionRecord selection = null;
private static POILogger log = POILogFactory.getLogger(Sheet.class);
private ArrayList columnSizes = null; // holds column info
protected ValueRecordsAggregate cells = null;
protected RowRecordsAggregate rows = null;
private Iterator valueRecIterator = null;
private Iterator rowRecIterator = null;
protected int eofLoc = 0;
protected ProtectRecord protect = null;
public static final byte PANE_LOWER_RIGHT = (byte)0;
public static final byte PANE_UPPER_RIGHT = (byte)1;
public static final byte PANE_LOWER_LEFT = (byte)2;
public static final byte PANE_UPPER_LEFT = (byte)3;
/**
* Creates new Sheet with no intialization --useless at this point
* @see #createSheet(List,int,int)
*/
public Sheet()
{
}
/**
* read support (offset used as starting point for search) for low level
* API. Pass in an array of Record objects, the sheet number (0 based) and
* a record offset (should be the location of the sheets BOF record). A Sheet
* object is constructed and passed back with all of its initialization set
* to the passed in records and references to those records held. This function
* is normally called via Workbook.
*
* @param recs array containing those records in the sheet in sequence (normally obtained from RecordFactory)
* @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
* @param offset of the sheet's BOF record
*
* @return Sheet object with all values set to those read from the file
*
* @see org.apache.poi.hssf.model.Workbook
* @see org.apache.poi.hssf.record.Record
*/
public static Sheet createSheet(List recs, int sheetnum, int offset)
{
log.logFormatted(log.DEBUG,
"Sheet createSheet (existing file) with %",
new Integer(recs.size()));
Sheet retval = new Sheet();
ArrayList records = new ArrayList(recs.size() / 5);
boolean isfirstcell = true;
boolean isfirstrow = true;
int bofEofNestingLevel = 0;
for (int k = offset; k < recs.size(); k++)
{
Record rec = ( Record ) recs.get(k);
if (rec.getSid() == LabelRecord.sid)
{
log.log(log.DEBUG, "Hit label record.");
retval.containsLabels = true;
}
else if (rec.getSid() == BOFRecord.sid)
{
bofEofNestingLevel++;
log.log(log.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
}
else if (rec.getSid() == EOFRecord.sid)
{
--bofEofNestingLevel;
log.log(log.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
if (bofEofNestingLevel == 0) {
records.add(rec);
retval.eofLoc = k;
break;
}
}
else if (rec.getSid() == DimensionsRecord.sid)
{
retval.dims = ( DimensionsRecord ) rec;
retval.dimsloc = records.size();
}
else if (rec.getSid() == MergeCellsRecord.sid)
{
retval.mergedRecords.add(rec);
retval.merged = ( MergeCellsRecord ) rec;
retval.mergedLocs.add(new Integer(k - offset));
retval.numMergedRegions += retval.merged.getNumAreas();
}
else if (rec.getSid() == ColumnInfoRecord.sid)
{
if (retval.columnSizes == null)
{
retval.columnSizes = new ArrayList();
}
retval.columnSizes.add(rec);
}
else if (rec.getSid() == DefaultColWidthRecord.sid)
{
retval.defaultcolwidth = ( DefaultColWidthRecord ) rec;
}
else if (rec.getSid() == DefaultRowHeightRecord.sid)
{
retval.defaultrowheight = ( DefaultRowHeightRecord ) rec;
}
else if ( rec.isValue() && bofEofNestingLevel == 1 )
{
if ( isfirstcell )
{
retval.cells = new ValueRecordsAggregate();
rec = retval.cells;
retval.cells.construct( k, recs );
isfirstcell = false;
}
else
{
rec = null;
}
}
else if ( rec.getSid() == StringRecord.sid )
{
rec = null;
}
else if ( rec.getSid() == RowRecord.sid )
{
RowRecord row = (RowRecord)rec;
if (!isfirstrow) rec = null; //only add the aggregate once
if ( isfirstrow )
{
retval.rows = new RowRecordsAggregate();
rec = retval.rows;
isfirstrow = false;
}
retval.rows.insertRow(row);
}
else if ( rec.getSid() == PrintGridlinesRecord.sid )
{
retval.printGridlines = (PrintGridlinesRecord) rec;
}
else if ( rec.getSid() == HeaderRecord.sid && bofEofNestingLevel == 1)
{
retval.header = (HeaderRecord) rec;
}
else if ( rec.getSid() == FooterRecord.sid && bofEofNestingLevel == 1)
{
retval.footer = (FooterRecord) rec;
}
else if ( rec.getSid() == PrintSetupRecord.sid && bofEofNestingLevel == 1)
{
retval.printSetup = (PrintSetupRecord) rec;
}
else if ( rec.getSid() == LeftMarginRecord.sid)
{
retval.getMargins()[LeftMargin] = (LeftMarginRecord) rec;
}
else if ( rec.getSid() == RightMarginRecord.sid)
{
retval.getMargins()[RightMargin] = (RightMarginRecord) rec;
}
else if ( rec.getSid() == TopMarginRecord.sid)
{
retval.getMargins()[TopMargin] = (TopMarginRecord) rec;
}
else if ( rec.getSid() == BottomMarginRecord.sid)
{
retval.getMargins()[BottomMargin] = (BottomMarginRecord) rec;
}
else if ( rec.getSid() == SelectionRecord.sid )
{
retval.selection = (SelectionRecord) rec;
}
else if ( rec.getSid() == WindowTwoRecord.sid )
{
retval.windowTwo = (WindowTwoRecord) rec;
}
else if ( rec.getSid() == ProtectRecord.sid )
{
retval.protect = (ProtectRecord) rec;
}
if (rec != null)
{
records.add(rec);
}
}
retval.records = records;
if (retval.rows == null)
{
retval.rows = new RowRecordsAggregate();
}
if (retval.cells == null)
{
retval.cells = new ValueRecordsAggregate();
}
log.log(log.DEBUG, "sheet createSheet (existing file) exited");
return retval;
}
/**
* 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 <b>Record</b> 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 Sheet cloneSheet()
{
ArrayList clonedRecords = new ArrayList(this.records.size());
for (int i=0; i<this.records.size();i++) {
Record rec = (Record)((Record)this.records.get(i)).clone();
//Need to pull out the Row record and the Value records from their
//Aggregates.
//This is probably the best way to do it since we probably dont want the createSheet
//To cater for these artificial Record types
if (rec instanceof RowRecordsAggregate) {
RowRecordsAggregate rrAgg = (RowRecordsAggregate)rec;
for (Iterator rowIter = rrAgg.getIterator();rowIter.hasNext();) {
Record rowRec = (Record)rowIter.next();
clonedRecords.add(rowRec);
}
} else if (rec instanceof ValueRecordsAggregate) {
ValueRecordsAggregate vrAgg = (ValueRecordsAggregate)rec;
for (Iterator cellIter = vrAgg.getIterator();cellIter.hasNext();) {
Record valRec = (Record)cellIter.next();
clonedRecords.add(valRec);
}
} else if (rec instanceof FormulaRecordAggregate) {
FormulaRecordAggregate fmAgg = (FormulaRecordAggregate)rec;
Record fmAggRec = fmAgg.getFormulaRecord();
if (fmAggRec != null)
clonedRecords.add(fmAggRec);
fmAggRec = fmAgg.getStringRecord();
if (fmAggRec != null)
clonedRecords.add(fmAggRec);
} else {
clonedRecords.add(rec);
}
}
return createSheet(clonedRecords, 0, 0);
}
/**
* read support (offset = 0) Same as createSheet(Record[] recs, int, int)
* only the record offset is assumed to be 0.
*
* @param records array containing those records in the sheet in sequence (normally obtained from RecordFactory)
* @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release)
* @return Sheet object
*/
public static Sheet createSheet(List records, int sheetnum)
{
log.log(log.DEBUG,
"Sheet createSheet (exisiting file) assumed offset 0");
return createSheet(records, sheetnum, 0);
}
/**
* 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".
*
* @return Sheet object with all values set to defaults
*/
public static Sheet createSheet()
{
log.log(log.DEBUG, "Sheet createsheet from scratch called");
Sheet retval = new Sheet();
ArrayList records = new ArrayList(30);
records.add(retval.createBOF());
// records.add(retval.createIndex());
records.add(retval.createCalcMode());
records.add(retval.createCalcCount() );
records.add( retval.createRefMode() );
records.add( retval.createIteration() );
records.add( retval.createDelta() );
records.add( retval.createSaveRecalc() );
records.add( retval.createPrintHeaders() );
retval.printGridlines = (PrintGridlinesRecord) retval.createPrintGridlines();
records.add( retval.printGridlines );
retval.gridset = (GridsetRecord) retval.createGridset();
records.add( retval.gridset );
records.add( retval.createGuts() );
retval.defaultrowheight =
(DefaultRowHeightRecord) retval.createDefaultRowHeight();
records.add( retval.defaultrowheight );
records.add( retval.createWSBool() );
retval.header = (HeaderRecord) retval.createHeader();
records.add( retval.header );
retval.footer = (FooterRecord) retval.createFooter();
records.add( retval.footer );
records.add( retval.createHCenter() );
records.add( retval.createVCenter() );
retval.printSetup = (PrintSetupRecord) retval.createPrintSetup();
records.add( retval.printSetup );
retval.defaultcolwidth =
(DefaultColWidthRecord) retval.createDefaultColWidth();
records.add( retval.defaultcolwidth);
retval.dims = ( DimensionsRecord ) retval.createDimensions();
retval.dimsloc = 19;
records.add(retval.dims);
records.add(retval.windowTwo = retval.createWindowTwo());
retval.setLoc(records.size() - 1);
retval.selection =
(SelectionRecord) retval.createSelection();
records.add(retval.selection);
retval.protect = (ProtectRecord) retval.createProtect();
records.add(retval.protect);
records.add(retval.createEOF());
retval.records = records;
log.log(log.DEBUG, "Sheet createsheet from scratch exit");
return retval;
}
private void checkCells()
{
if (cells == null)
{
cells = new ValueRecordsAggregate();
records.add(getDimsLoc() + 1, cells);
}
}
private void checkRows()
{
if (rows == null)
{
rows = new RowRecordsAggregate();
records.add(getDimsLoc() + 1, rows);
}
}
//public int addMergedRegion(short rowFrom, short colFrom, short rowTo,
public int addMergedRegion(int rowFrom, short colFrom, int rowTo,
short colTo)
{
if (merged == null || merged.getNumAreas() == 1027)
{
merged = ( MergeCellsRecord ) createMergedCells();
mergedRecords.add(merged);
mergedLocs.add(new Integer(records.size() - 1));
records.add(records.size() - 1, merged);
}
merged.addArea(rowFrom, colFrom, rowTo, colTo);
return numMergedRegions++;
}
public void removeMergedRegion(int index)
{
//safety checks
if (index >= numMergedRegions || mergedRecords.size() == 0)
return;
int pos = 0;
int startNumRegions = 0;
//optimisation for current record
if (numMergedRegions - index < merged.getNumAreas())
{
pos = mergedRecords.size() - 1;
startNumRegions = numMergedRegions - merged.getNumAreas();
}
else
{
for (int n = 0; n < mergedRecords.size(); n++)
{
MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n);
if (startNumRegions + record.getNumAreas() > index)
{
pos = n;
break;
}
startNumRegions += record.getNumAreas();
}
}
MergeCellsRecord rec = (MergeCellsRecord) mergedRecords.get(pos);
rec.removeAreaAt(index - startNumRegions);
numMergedRegions--;
if (rec.getNumAreas() == 0)
{
mergedRecords.remove(pos);
if (merged == rec) {
//pull up the LAST record for operations when we finally
//support continue records for mergedRegions
if (mergedRecords.size() > 0) {
merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1);
} else {
merged = null;
}
}
int removePos = ((Integer) mergedLocs.get(pos)).intValue();
records.remove(removePos);
mergedLocs.remove(pos);
}
}
public MergeCellsRecord.MergedRegion getMergedRegionAt(int index)
{
//safety checks
if (index >= numMergedRegions || mergedRecords.size() == 0)
return null;
int pos = 0;
int startNumRegions = 0;
//optimisation for current record
if (numMergedRegions - index < merged.getNumAreas())
{
pos = mergedRecords.size() - 1;
startNumRegions = numMergedRegions - merged.getNumAreas();
}
else
{
for (int n = 0; n < mergedRecords.size(); n++)
{
MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n);
if (startNumRegions + record.getNumAreas() > index)
{
pos = n;
break;
}
startNumRegions += record.getNumAreas();
}
}
return ((MergeCellsRecord) mergedRecords.get(pos)).getAreaAt(index - startNumRegions);
}
public int getNumMergedRegions()
{
return numMergedRegions;
}
/**
* This is basically a kludge to deal with the now obsolete Label records. If
* you have to read in a sheet that contains Label records, be aware that the rest
* of the API doesn't deal with them, the low level structure only provides read-only
* semi-immutable structures (the sets are there for interface conformance with NO
* impelmentation). In short, you need to call this function passing it a reference
* to the Workbook object. All labels will be converted to LabelSST records and their
* contained strings will be written to the Shared String tabel (SSTRecord) within
* the Workbook.
*
* @param wb sheet's matching low level Workbook structure containing the SSTRecord.
* @see org.apache.poi.hssf.record.LabelRecord
* @see org.apache.poi.hssf.record.LabelSSTRecord
* @see org.apache.poi.hssf.record.SSTRecord
*/
public void convertLabelRecords(Workbook wb)
{
log.log(log.DEBUG, "convertLabelRecords called");
if (containsLabels)
{
for (int k = 0; k < records.size(); k++)
{
Record rec = ( Record ) records.get(k);
if (rec.getSid() == LabelRecord.sid)
{
LabelRecord oldrec = ( LabelRecord ) rec;
records.remove(k);
LabelSSTRecord newrec = new LabelSSTRecord();
int stringid =
wb.addSSTString(oldrec.getValue());
newrec.setRow(oldrec.getRow());
newrec.setColumn(oldrec.getColumn());
newrec.setXFIndex(oldrec.getXFIndex());
newrec.setSSTIndex(stringid);
records.add(k, newrec);
}
}
}
log.log(log.DEBUG, "convertLabelRecords exit");
}
/**
* Returns the number of low level binary records in this sheet. This adjusts things for the so called
* AgregateRecords.
*
* @see org.apache.poi.hssf.record.Record
*/
public int getNumRecords()
{
checkCells();
checkRows();
log.log(log.DEBUG, "Sheet.getNumRecords");
log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[]
{
records.size(), cells.getPhysicalNumberOfCells(),
rows.getPhysicalNumberOfRows(),
records.size() + cells.getPhysicalNumberOfCells()
+ rows.getPhysicalNumberOfRows() - 2
});
return records.size() + cells.getPhysicalNumberOfCells()
+ rows.getPhysicalNumberOfRows() - 2;
}
/**
* 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.
*
* @see org.apache.poi.hssf.record.DimensionsRecord
*/
//public void setDimensions(short firstrow, short firstcol, short lastrow,
public void setDimensions(int firstrow, short firstcol, int lastrow,
short lastcol)
{
log.log(log.DEBUG, "Sheet.setDimensions");
log.log(log.DEBUG,
(new StringBuffer("firstrow")).append(firstrow)
.append("firstcol").append(firstcol).append("lastrow")
.append(lastrow).append("lastcol").append(lastcol)
.toString());
dims.setFirstCol(firstcol);
dims.setFirstRow(firstrow);
dims.setLastCol(lastcol);
dims.setLastRow(lastrow);
log.log(log.DEBUG, "Sheet.setDimensions exiting");
}
/**
* set the locator for where we should look for the next value record. The
* algorythm will actually start here and find the correct location so you
* can set this to 0 and watch performance go down the tubes but it will work.
* After a value is set this is automatically advanced. Its also set by the
* create method. So you probably shouldn't mess with this unless you have
* a compelling reason why or the help for the method you're calling says so.
* Check the other methods for whether they care about
* the loc pointer. Many of the "modify" and "remove" methods re-initialize this
* to "dimsloc" which is the location of the Dimensions Record and presumably the
* start of the value section (at or around 19 dec).
*
* @param loc the record number to start at
*
*/
public void setLoc(int loc)
{
valueRecIterator = null;
log.log(log.DEBUG, "sheet.setLoc(): " + loc);
this.loc = loc;
}
/**
* Returns the location pointer to the first record to look for when adding rows/values
*
*/
public int getLoc()
{
log.log(log.DEBUG, "sheet.getLoc():" + loc);
return loc;
}
/**
* Set the preoffset when using DBCELL records (currently unused) - this is
* the position of this sheet within the whole file.
*
* @param offset the offset of the sheet's BOF within the file.
*/
public void setPreOffset(int offset)
{
this.preoffset = offset;
}
/**
* get the preoffset when using DBCELL records (currently unused) - this is
* the position of this sheet within the whole file.
*
* @return offset the offset of the sheet's BOF within the file.
*/
public int getPreOffset()
{
return preoffset;
}
/**
* Serializes all records in the sheet into one big byte array. Use this to write
* the sheet out.
*
* @return byte[] array containing the binary representation of the records in this sheet
*
*/
public byte [] serialize()
{
log.log(log.DEBUG, "Sheet.serialize");
// addDBCellRecords();
byte[] retval = null;
// ArrayList bytes = new ArrayList(4096);
int arraysize = getSize();
int pos = 0;
// for (int k = 0; k < records.size(); k++)
// {
// bytes.add((( Record ) records.get(k)).serialize());
//
// }
// for (int k = 0; k < bytes.size(); k++)
// {
// arraysize += (( byte [] ) bytes.get(k)).length;
// log.debug((new StringBuffer("arraysize=")).append(arraysize)
// .toString());
// }
retval = new byte[ arraysize ];
for (int k = 0; k < records.size(); k++)
{
// byte[] rec = (( byte [] ) bytes.get(k));
// System.arraycopy(rec, 0, retval, pos, rec.length);
pos += (( Record ) records.get(k)).serialize(pos,
retval); // rec.length;
}
log.log(log.DEBUG, "Sheet.serialize returning " + retval);
return retval;
}
/**
* Serializes all records in the sheet into one big byte array. Use this to write
* the sheet out.
*
* @param offset to begin write at
* @param data array containing the binary representation of the records in this sheet
*
*/
public int serialize(int offset, byte [] data)
{
log.log(log.DEBUG, "Sheet.serialize using offsets");
// addDBCellRecords();
// ArrayList bytes = new ArrayList(4096);
// int arraysize = getSize(); // 0;
int pos = 0;
// for (int k = 0; k < records.size(); k++)
// {
// bytes.add((( Record ) records.get(k)).serialize());
//
// }
// for (int k = 0; k < bytes.size(); k++)
// {
// arraysize += (( byte [] ) bytes.get(k)).length;
// log.debug((new StringBuffer("arraysize=")).append(arraysize)
// .toString());
// }
for (int k = 0; k < records.size(); k++)
{
// byte[] rec = (( byte [] ) bytes.get(k));
// System.arraycopy(rec, 0, data, offset + pos, rec.length);
Record record = (( Record ) records.get(k));
//uncomment to test record sizes
// byte[] data2 = new byte[record.getRecordSize()];
// record.serialize(0, data2 ); // rec.length;
// if (LittleEndian.getUShort(data2, 2) != record.getRecordSize() - 4
// && record instanceof RowRecordsAggregate == false && record instanceof ValueRecordsAggregate == false)
// throw new RuntimeException("Blah!!!");
pos += record.serialize(pos + offset, data ); // rec.length;
}
log.log(log.DEBUG, "Sheet.serialize returning ");
return pos;
}
/**
* Create a row record. (does not add it to the records contained in this sheet)
*
* @param row number
* @return RowRecord created for the passed in row number
* @see org.apache.poi.hssf.record.RowRecord
*/
public RowRecord createRow(int row)
{
log.log(log.DEBUG, "create row number " + row);
RowRecord rowrec = new RowRecord();
//rowrec.setRowNumber(( short ) row);
rowrec.setRowNumber(row);
rowrec.setHeight(( short ) 0xff);
rowrec.setOptimize(( short ) 0x0);
rowrec.setOptionFlags(( short ) 0x0);
rowrec.setXFIndex(( short ) 0x0);
return rowrec;
}
/**
* Create a LABELSST Record (does not add it to the records contained in this sheet)
*
* @param row the row the LabelSST is a member of
* @param col the column the LabelSST defines
* @param index the index of the string within the SST (use workbook addSSTString method)
* @return LabelSSTRecord newly created containing your SST Index, row,col.
* @see org.apache.poi.hssf.record.SSTRecord
*/
//public LabelSSTRecord createLabelSST(short row, short col, int index)
public LabelSSTRecord createLabelSST(int row, short col, int index)
{
log.logFormatted(log.DEBUG, "create labelsst row,col,index %,%,%",
new int[]
{
row, col, index
});
LabelSSTRecord rec = new LabelSSTRecord();
rec.setRow(row);
rec.setColumn(col);
rec.setSSTIndex(index);
rec.setXFIndex(( short ) 0x0f);
return rec;
}
/**
* Create a NUMBER Record (does not add it to the records contained in this sheet)
*
* @param row the row the NumberRecord is a member of
* @param col the column the NumberRecord defines
* @param value for the number record
*
* @return NumberRecord for that row, col containing that value as added to the sheet
*/
//public NumberRecord createNumber(short row, short col, double value)
public NumberRecord createNumber(int row, short col, double value)
{
log.logFormatted(log.DEBUG, "create number row,col,value %,%,%",
new double[]
{
row, col, value
});
NumberRecord rec = new NumberRecord();
//rec.setRow(( short ) row);
rec.setRow(row);
rec.setColumn(col);
rec.setValue(value);
rec.setXFIndex(( short ) 0x0f);
return rec;
}
/**
* create a BLANK record (does not add it to the records contained in this sheet)
*
* @param row - the row the BlankRecord is a member of
* @param col - the column the BlankRecord is a member of
*/
//public BlankRecord createBlank(short row, short col)
public BlankRecord createBlank(int row, short col)
{
//log.logFormatted(log.DEBUG, "create blank row,col %,%", new short[]
log.logFormatted(log.DEBUG, "create blank row,col %,%", new int[]
{
row, col
});
BlankRecord rec = new BlankRecord();
//rec.setRow(( short ) row);
rec.setRow(row);
rec.setColumn(col);
rec.setXFIndex(( short ) 0x0f);
return rec;
}
/**
* Attempts to parse the formula into PTGs and create a formula record
* DOES NOT WORK YET
*
* @param row - the row for the formula record
* @param col - the column of the formula record
* @param formula - a String representing the formula. To be parsed to PTGs
* @return bogus/useless formula record
*/
//public FormulaRecord createFormula(short row, short col, String formula)
public FormulaRecord createFormula(int row, short col, String formula)
{
log.logFormatted(log.DEBUG, "create formula row,col,formula %,%,%",
//new short[]
new int[]
{
row, col
}, formula);
FormulaRecord rec = new FormulaRecord();
rec.setRow(row);
rec.setColumn(col);
rec.setOptions(( short ) 2);
rec.setValue(0);
rec.setXFIndex(( short ) 0x0f);
FormulaParser fp = new FormulaParser(formula,null); //fix - do we need this method?
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
int size = 0;
for (int k = 0; k < ptg.length; k++)
{
size += ptg[ k ].getSize();
rec.pushExpressionToken(ptg[ k ]);
}
rec.setExpressionLength(( short ) size);
return rec;
}
/**
* Adds a value record to the sheet's contained binary records
* (i.e. LabelSSTRecord or NumberRecord).
* <P>
* 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.
*
* @param row the row to add the cell value to
* @param col the cell value record itself.
*/
//public void addValueRecord(short row, CellValueRecordInterface col)
public void addValueRecord(int row, CellValueRecordInterface col)
{
checkCells();
log.logFormatted(log.DEBUG, "add value record row,loc %,%", new int[]
{
row, loc
});
DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
if (col.getColumn() > d.getLastCol())
{
d.setLastCol(( short ) (col.getColumn() + 1));
}
if (col.getColumn() < d.getFirstCol())
{
d.setFirstCol(col.getColumn());
}
cells.insertCell(col);
/*
* for (int k = loc; k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* if (rec.getSid() == RowRecord.sid)
* {
* RowRecord rowrec = ( RowRecord ) rec;
*
* if (rowrec.getRowNumber() == col.getRow())
* {
* records.add(k + 1, col);
* loc = k;
* if (rowrec.getLastCol() <= col.getColumn())
* {
* rowrec.setLastCol((( short ) (col.getColumn() + 1)));
* }
* break;
* }
* }
* }
*/
}
/**
* remove a value record from the records array.
*
* This method is not loc sensitive, it resets loc to = dimsloc so no worries.
*
* @param row - the row of the value record you wish to remove
* @param col - a record supporting the CellValueRecordInterface.
* @see org.apache.poi.hssf.record.CellValueRecordInterface
*/
//public void removeValueRecord(short row, CellValueRecordInterface col)
public void removeValueRecord(int row, CellValueRecordInterface col)
{
checkCells();
log.logFormatted(log.DEBUG, "remove value record row,dimsloc %,%",
new int[]
{
row, dimsloc
});
loc = dimsloc;
cells.removeCell(col);
/*
* for (int k = loc; k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* // checkDimsLoc(rec,k);
* if (rec.isValue())
* {
* CellValueRecordInterface cell =
* ( CellValueRecordInterface ) rec;
*
* if ((cell.getRow() == col.getRow())
* && (cell.getColumn() == col.getColumn()))
* {
* records.remove(k);
* break;
* }
* }
* }
*/
}
/**
* replace a value record from the records array.
*
* This method is not loc sensitive, it resets loc to = dimsloc so no worries.
*
* @param newval - 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)
{
checkCells();
setLoc(dimsloc);
log.log(log.DEBUG, "replaceValueRecord ");
cells.insertCell(newval);
/*
* CellValueRecordInterface oldval = getNextValueRecord();
*
* while (oldval != null)
* {
* if (oldval.isEqual(newval))
* {
* records.set(( short ) (getLoc() - 1), newval);
* return;
* }
* oldval = getNextValueRecord();
* }
* addValueRecord(newval.getRow(), newval);
* setLoc(dimsloc);
*/
}
/**
* Adds a row record to the sheet
*
* <P>
* 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.
*
* @param row the row record to be added
* @see #setLoc(int)
*/
public void addRow(RowRecord row)
{
checkRows();
log.log(log.DEBUG, "addRow ");
DimensionsRecord d = ( DimensionsRecord ) records.get(getDimsLoc());
if (row.getRowNumber() > d.getLastRow())
{
d.setLastRow(row.getRowNumber() + 1);
}
if (row.getRowNumber() < d.getFirstRow())
{
d.setFirstRow(row.getRowNumber());
}
//IndexRecord index = null;
//If the row exists remove it, so that any cells attached to the row are removed
RowRecord existingRow = rows.getRow(row.getRowNumber());
if (existingRow != null)
rows.removeRow(existingRow);
rows.insertRow(row);
/*
* for (int k = loc; k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* if (rec.getSid() == IndexRecord.sid)
* {
* index = ( IndexRecord ) rec;
* }
* if (rec.getSid() == RowRecord.sid)
* {
* RowRecord rowrec = ( RowRecord ) rec;
*
* if (rowrec.getRowNumber() > row.getRowNumber())
* {
* records.add(k, row);
* loc = k;
* break;
* }
* }
* if (rec.getSid() == WindowTwoRecord.sid)
* {
* records.add(k, row);
* loc = k;
* break;
* }
* }
* if (index != null)
* {
* if (index.getLastRowAdd1() <= row.getRowNumber())
* {
* index.setLastRowAdd1(row.getRowNumber() + 1);
* }
* }
*/
log.log(log.DEBUG, "exit addRow");
}
/**
* Removes a row record
*
* This method is not loc sensitive, it resets loc to = dimsloc so no worries.
*
* @param row the row record to remove
*/
public void removeRow(RowRecord row)
{
checkRows();
// IndexRecord index = null;
setLoc(getDimsLoc());
rows.removeRow(row);
/*
* for (int k = loc; k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* // checkDimsLoc(rec,k);
* if (rec.getSid() == RowRecord.sid)
* {
* RowRecord rowrec = ( RowRecord ) rec;
*
* if (rowrec.getRowNumber() == row.getRowNumber())
* {
* records.remove(k);
* break;
* }
* }
* if (rec.getSid() == WindowTwoRecord.sid)
* {
* break;
* }
* }
*/
}
/**
* get the NEXT value record (from LOC). The first record that is a value record
* (starting at LOC) will be returned.
*
* <P>
* 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.
*
* @return CellValueRecordInterface representing the next value record or NULL if there are no more
* @see #setLoc(int)
*/
public CellValueRecordInterface getNextValueRecord()
{
log.log(log.DEBUG, "getNextValue loc= " + loc);
if (valueRecIterator == null)
{
valueRecIterator = cells.getIterator();
}
if (!valueRecIterator.hasNext())
{
return null;
}
return ( CellValueRecordInterface ) valueRecIterator.next();
/*
* if (this.getLoc() < records.size())
* {
* for (int k = getLoc(); k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* this.setLoc(k + 1);
* if (rec instanceof CellValueRecordInterface)
* {
* return ( CellValueRecordInterface ) rec;
* }
* }
* }
* return null;
*/
}
/**
* get the NEXT RowRecord or CellValueRecord(from LOC). The first record that
* is a Row record or CellValueRecord(starting at LOC) will be returned.
* <P>
* 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.
*
* @return RowRecord representing the next row record or CellValueRecordInterface
* representing the next cellvalue or NULL if there are no more
* @see #setLoc(int)
*
*/
/* public Record getNextRowOrValue()
{
log.debug((new StringBuffer("getNextRow loc= ")).append(loc)
.toString());
if (this.getLoc() < records.size())
{
for (int k = this.getLoc(); k < records.size(); k++)
{
Record rec = ( Record ) records.get(k);
this.setLoc(k + 1);
if (rec.getSid() == RowRecord.sid)
{
return rec;
}
else if (rec.isValue())
{
return rec;
}
}
}
return null;
}
*/
/**
* get the NEXT RowRecord (from LOC). The first record that is a Row record
* (starting at LOC) will be returned.
* <P>
* 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.
*
* @return RowRecord representing the next row record or NULL if there are no more
* @see #setLoc(int)
*
*/
public RowRecord getNextRow()
{
log.log(log.DEBUG, "getNextRow loc= " + loc);
if (rowRecIterator == null)
{
rowRecIterator = rows.getIterator();
}
if (!rowRecIterator.hasNext())
{
return null;
}
return ( RowRecord ) rowRecIterator.next();
/* if (this.getLoc() < records.size())
{
for (int k = this.getLoc(); k < records.size(); k++)
{
Record rec = ( Record ) records.get(k);
this.setLoc(k + 1);
if (rec.getSid() == RowRecord.sid)
{
return ( RowRecord ) rec;
}
}
}*/
}
/**
* 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.
* <P>
* 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.
*
* @param rownum which row to return (careful with LOC)
* @return RowRecord representing the next row record or NULL if there are no more
* @see #setLoc(int)
*
*/
//public RowRecord getRow(short rownum)
public RowRecord getRow(int rownum)
{
log.log(log.DEBUG, "getNextRow loc= " + loc);
return rows.getRow(rownum);
/*
* if (this.getLoc() < records.size())
* {
* for (int k = this.getLoc(); k < records.size(); k++)
* {
* Record rec = ( Record ) records.get(k);
*
* this.setLoc(k + 1);
* if (rec.getSid() == RowRecord.sid)
* {
* if ((( RowRecord ) rec).getRowNumber() == rownum)
* {
* return ( RowRecord ) rec;
* }
* }
* }
* }
*/
// return null;
}
/**
* Not currently used method to calculate and add dbcell records
*
*/
public void addDBCellRecords()
{
int offset = 0;
int recnum = 0;
int rownum = 0;
//int lastrow = 0;
//long lastrowoffset = 0;
IndexRecord index = null;
// ArrayList rowOffsets = new ArrayList();
IntList rowOffsets = new IntList();
for (recnum = 0; recnum < records.size(); recnum++)
{
Record rec = ( Record ) records.get(recnum);
if (rec.getSid() == IndexRecord.sid)
{
index = ( IndexRecord ) rec;
}
if (rec.getSid() != RowRecord.sid)
{
offset += rec.serialize().length;
}
else
{
break;
}
}
// First Row Record
for (; recnum < records.size(); recnum++)
{
Record rec = ( Record ) records.get(recnum);
if (rec.getSid() == RowRecord.sid)
{
rownum++;
rowOffsets.add(offset);
if ((rownum % 32) == 0)
{
// if this is the last rec in a dbcell block
// find the next row or last value record
for (int rn = recnum; rn < records.size(); rn++)
{
rec = ( Record ) records.get(rn);
if ((!rec.isInValueSection())
|| (rec.getSid() == RowRecord.sid))
{
// here is the next row or last value record
records.add(rn,
createDBCell(offset, rowOffsets,
index));
recnum = rn;
break;
}
}
}
else
{
}
}
if (!rec.isInValueSection())
{
records.add(recnum, createDBCell(offset, rowOffsets, index));
break;
}
offset += rec.serialize().length;
}
}
/** not currently used */
private DBCellRecord createDBCell(int offset, IntList rowoffsets,
IndexRecord index)
{
DBCellRecord rec = new DBCellRecord();
rec.setRowOffset(offset - rowoffsets.get(0));
// test hack
rec.addCellOffset(( short ) 0x0);
// end test hack
addDbCellToIndex(offset, index);
return rec;
}
/** not currently used */
private void addDbCellToIndex(int offset, IndexRecord index)
{
int numdbcells = index.getNumDbcells() + 1;
index.addDbcell(offset + preoffset);
// stupid but whenever we add an offset that causes everything to be shifted down 4
for (int k = 0; k < numdbcells; k++)
{
int dbval = index.getDbcellAt(k);
index.setDbcell(k, dbval + 4);
}
}
/**
* creates the BOF record
* @see org.apache.poi.hssf.record.BOFRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a BOFRecord
*/
protected Record createBOF()
{
BOFRecord retval = new BOFRecord();
retval.setVersion(( short ) 0x600);
retval.setType(( short ) 0x010);
// retval.setBuild((short)0x10d3);
retval.setBuild(( short ) 0x0dbb);
retval.setBuildYear(( short ) 1996);
retval.setHistoryBitMask(0xc1);
retval.setRequiredVersion(0x6);
return retval;
}
/**
* creates the Index record - not currently used
* @see org.apache.poi.hssf.record.IndexRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a IndexRecord
*/
protected Record createIndex()
{
IndexRecord retval = new IndexRecord();
retval.setFirstRow(0); // must be set explicitly
retval.setLastRowAdd1(0);
return retval;
}
/**
* creates the CalcMode record and sets it to 1 (automatic formula caculation)
* @see org.apache.poi.hssf.record.CalcModeRecord
* @see org.apache.poi.hssf.record.Record
* @return 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)
* @see org.apache.poi.hssf.record.CalcCountRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a CalcCountRecord
*/
protected Record createCalcCount()
{
CalcCountRecord retval = new CalcCountRecord();
retval.setIterations(( short ) 0x64); // default 64 iterations
return retval;
}
/**
* creates the RefMode record and sets it to A1 Mode (default reference mode)
* @see org.apache.poi.hssf.record.RefModeRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a RefModeRecord
*/
protected Record createRefMode()
{
RefModeRecord retval = new RefModeRecord();
retval.setMode(retval.USE_A1_MODE);
return retval;
}
/**
* creates the Iteration record and sets it to false (don't iteratively calculate formulas)
* @see org.apache.poi.hssf.record.IterationRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a IterationRecord
*/
protected Record createIteration()
{
IterationRecord retval = new IterationRecord();
retval.setIteration(false);
return retval;
}
/**
* creates the Delta record and sets it to 0.0010 (default accuracy)
* @see org.apache.poi.hssf.record.DeltaRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a DeltaRecord
*/
protected Record createDelta()
{
DeltaRecord retval = new DeltaRecord();
retval.setMaxChange(0.0010);
return retval;
}
/**
* creates the SaveRecalc record and sets it to true (recalculate before saving)
* @see org.apache.poi.hssf.record.SaveRecalcRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a SaveRecalcRecord
*/
protected Record createSaveRecalc()
{
SaveRecalcRecord retval = new SaveRecalcRecord();
retval.setRecalc(true);
return retval;
}
/**
* creates the PrintHeaders record and sets it to false (we don't create headers yet so why print them)
* @see org.apache.poi.hssf.record.PrintHeadersRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a PrintHeadersRecord
*/
protected Record createPrintHeaders()
{
PrintHeadersRecord retval = new PrintHeadersRecord();
retval.setPrintHeaders(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
*
* @see org.apache.poi.hssf.record.PrintGridlinesRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a PrintGridlinesRecord
*/
protected Record createPrintGridlines()
{
PrintGridlinesRecord retval = new PrintGridlinesRecord();
retval.setPrintGridlines(false);
return retval;
}
/**
* creates the Gridset record and sets it to true (user has mucked with the gridlines)
* @see org.apache.poi.hssf.record.GridsetRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a GridsetRecord
*/
protected Record createGridset()
{
GridsetRecord retval = new GridsetRecord();
retval.setGridset(true);
return retval;
}
/**
* creates the Guts record and sets leftrow/topcol guttter and rowlevelmax/collevelmax to 0
* @see org.apache.poi.hssf.record.GutsRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a GutsRecordRecord
*/
protected Record createGuts()
{
GutsRecord retval = new GutsRecord();
retval.setLeftRowGutter(( short ) 0);
retval.setTopColGutter(( short ) 0);
retval.setRowLevelMax(( short ) 0);
retval.setColLevelMax(( short ) 0);
return retval;
}
/**
* creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff
* @see org.apache.poi.hssf.record.DefaultRowHeightRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a DefaultRowHeightRecord
*/
protected Record createDefaultRowHeight()
{
DefaultRowHeightRecord retval = new DefaultRowHeightRecord();
retval.setOptionFlags(( short ) 0);
retval.setRowHeight(( 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.setWSBool1(( byte ) 0x4);
retval.setWSBool2(( byte ) 0xffffffc1);
return retval;
}
/**
* creates the Header Record and sets it to nothing/0 length
* @see org.apache.poi.hssf.record.HeaderRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a HeaderRecord
*/
protected Record createHeader()
{
HeaderRecord retval = new HeaderRecord();
retval.setHeaderLength(( byte ) 0);
retval.setHeader(null);
return retval;
}
/**
* creates the Footer Record and sets it to nothing/0 length
* @see org.apache.poi.hssf.record.FooterRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a FooterRecord
*/
protected Record createFooter()
{
FooterRecord retval = new FooterRecord();
retval.setFooterLength(( byte ) 0);
retval.setFooter(null);
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.setHCenter(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.setVCenter(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.setPaperSize(( short ) 1);
retval.setScale(( short ) 100);
retval.setPageStart(( short ) 1);
retval.setFitWidth(( short ) 1);
retval.setFitHeight(( short ) 1);
retval.setOptions(( short ) 2);
retval.setHResolution(( short ) 300);
retval.setVResolution(( short ) 300);
retval.setHeaderMargin( 0.5);
retval.setFooterMargin( 0.5);
retval.setCopies(( 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 Record createDefaultColWidth()
{
DefaultColWidthRecord retval = new DefaultColWidthRecord();
retval.setColWidth(( short ) 8);
return retval;
}
/**
* creates the ColumnInfo Record and sets it to a default column/width
* @see org.apache.poi.hssf.record.ColumnInfoRecord
* @return record containing a ColumnInfoRecord
*/
protected Record createColInfo()
{
ColumnInfoRecord retval = new ColumnInfoRecord();
retval.setColumnWidth(( short ) 0x8);
retval.setOptions(( short ) 6);
retval.setXFIndex(( short ) 0x0f);
return retval;
}
/**
* get the default column width for the sheet (if the columns do not define their own width)
* @return default column width
*/
public short getDefaultColumnWidth()
{
return defaultcolwidth.getColWidth();
}
/**
* get whether gridlines are printed.
* @return true if printed
*/
public boolean isGridsPrinted()
{
return !gridset.getGridset();
}
/**
* set whether gridlines printed or not.
* @param value True if gridlines printed.
*/
public void setGridsPrinted(boolean value)
{
gridset.setGridset(!value);
}
/**
* set the default column width for the sheet (if the columns do not define their own width)
* @param dcw default column width
*/
public void setDefaultColumnWidth(short dcw)
{
defaultcolwidth.setColWidth(dcw);
}
/**
* set the default row height for the sheet (if the rows do not define their own height)
*/
public void setDefaultRowHeight(short dch)
{
defaultrowheight.setRowHeight(dch);
}
/**
* get the default row height for the sheet (if the rows do not define their own height)
* @return default row height
*/
public short getDefaultRowHeight()
{
return defaultrowheight.getRowHeight();
}
/**
* get the width of a given column in units of 1/20th of a point width (twips?)
* @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/20th of a point (twips?)
*/
public short getColumnWidth(short column)
{
short retval = 0;
ColumnInfoRecord ci = null;
int k = 0;
if (columnSizes != null)
{
for (k = 0; k < columnSizes.size(); k++)
{
ci = ( ColumnInfoRecord ) columnSizes.get(k);
if ((ci.getFirstColumn() <= column)
&& (column <= ci.getLastColumn()))
{
break;
}
ci = null;
}
}
if (ci != null)
{
retval = ci.getColumnWidth();
}
else
{
retval = defaultcolwidth.getColWidth();
}
return retval;
}
/**
* set the width for a given column in 1/20th of a character width units
* @param column - the column number
* @param width (in units of 1/20th of a character width)
*/
public void setColumnWidth(short column, short width)
{
ColumnInfoRecord ci = null;
int k = 0;
if (columnSizes == null)
{
columnSizes = new ArrayList();
}
//int cioffset = getDimsLoc() - columnSizes.size();
for (k = 0; k < columnSizes.size(); k++)
{
ci = ( ColumnInfoRecord ) columnSizes.get(k);
if ((ci.getFirstColumn() <= column)
&& (column <= ci.getLastColumn()))
{
break;
}
ci = null;
}
if (ci != null)
{
if (ci.getColumnWidth() == width)
{
// do nothing...the cell's width is equal to what we're setting it to.
}
else if ((ci.getFirstColumn() == column)
&& (ci.getLastColumn() == column))
{ // if its only for this cell then
ci.setColumnWidth(width); // who cares, just change the width
}
else if ((ci.getFirstColumn() == column)
|| (ci.getLastColumn() == column))
{
// okay so the width is different but the first or last column == the column we'return setting
// we'll just divide the info and create a new one
if (ci.getFirstColumn() == column)
{
ci.setFirstColumn(( short ) (column + 1));
}
else
{
ci.setLastColumn(( short ) (column - 1));
}
ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
nci.setFirstColumn(column);
nci.setLastColumn(column);
nci.setOptions(ci.getOptions());
nci.setXFIndex(ci.getXFIndex());
nci.setColumnWidth(width);
columnSizes.add(k, nci);
records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
dimsloc++;
}
else{
//split to 3 records
short lastcolumn = ci.getLastColumn();
ci.setLastColumn(( short ) (column - 1));
ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
nci.setFirstColumn(column);
nci.setLastColumn(column);
nci.setOptions(ci.getOptions());
nci.setXFIndex(ci.getXFIndex());
nci.setColumnWidth(width);
columnSizes.add(k, nci);
records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
dimsloc++;
nci = ( ColumnInfoRecord ) createColInfo();
nci.setFirstColumn((short)(column+1));
nci.setLastColumn(lastcolumn);
nci.setOptions(ci.getOptions());
nci.setXFIndex(ci.getXFIndex());
nci.setColumnWidth(ci.getColumnWidth());
columnSizes.add(k, nci);
records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
dimsloc++;
}
}
else
{
// okay so there ISN'T a column info record that cover's this column so lets create one!
ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo();
nci.setFirstColumn(column);
nci.setLastColumn(column);
nci.setColumnWidth(width);
columnSizes.add(k, nci);
records.add((1 + getDimsLoc() - columnSizes.size()) + k, nci);
dimsloc++;
}
}
/**
* 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
*/
protected Record createDimensions()
{
DimensionsRecord retval = new DimensionsRecord();
retval.setFirstCol(( short ) 0);
retval.setLastRow(1); // one more than it is
retval.setFirstRow(0);
retval.setLastCol(( short ) 1); // one more than it is
return retval;
}
/**
* creates the WindowTwo Record and sets it to: <P>
* options = 0x6b6 <P>
* toprow = 0 <P>
* leftcol = 0 <P>
* headercolor = 0x40 <P>
* pagebreakzoom = 0x0 <P>
* normalzoom = 0x0 <p>
* @see org.apache.poi.hssf.record.WindowTwoRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a WindowTwoRecord
*/
protected WindowTwoRecord createWindowTwo()
{
WindowTwoRecord retval = new WindowTwoRecord();
retval.setOptions(( short ) 0x6b6);
retval.setTopRow(( short ) 0);
retval.setLeftCol(( short ) 0);
retval.setHeaderColor(0x40);
retval.setPageBreakZoom(( short ) 0);
retval.setNormalZoom(( short ) 0);
return retval;
}
/**
* Creates the Selection record and sets it to nothing selected
*
* @see org.apache.poi.hssf.record.SelectionRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a SelectionRecord
*/
protected Record createSelection()
{
SelectionRecord retval = new SelectionRecord();
retval.setPane(( byte ) 0x3);
retval.setActiveCellCol(( short ) 0x0);
retval.setActiveCellRow(( short ) 0x0);
retval.setNumRefs(( short ) 0x0);
return retval;
}
/**
* Returns the active row
*
* @see org.apache.poi.hssf.record.SelectionRecord
* @return row the active row index
*/
public int getActiveCellRow()
{
if (selection == null)
{
return 0;
}
return selection.getActiveCellRow();
}
/**
* Sets the active row
*
* @param row the row index
* @see org.apache.poi.hssf.record.SelectionRecord
*/
public void setActiveCellRow(int row)
{
//shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
if (selection != null)
{
selection.setActiveCellRow(row);
}
}
/**
* Returns the active column
*
* @see org.apache.poi.hssf.record.SelectionRecord
* @return row the active column index
*/
public short getActiveCellCol()
{
if (selection == null)
{
return (short) 0;
}
return selection.getActiveCellCol();
}
/**
* Sets the active column
*
* @param col the column index
* @see org.apache.poi.hssf.record.SelectionRecord
*/
public void setActiveCellCol(short col)
{
//shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
if (selection != null)
{
selection.setActiveCellCol(col);
}
}
protected Record createMergedCells()
{
MergeCellsRecord retval = new MergeCellsRecord();
retval.setNumAreas(( short ) 0);
return retval;
}
/**
* creates the EOF record
* @see org.apache.poi.hssf.record.EOFRecord
* @see org.apache.poi.hssf.record.Record
* @return record containing a EOFRecord
*/
protected Record createEOF()
{
return new EOFRecord();
}
/**
* get the location of the DimensionsRecord (which is the last record before the value section)
* @return location in the array of records of the DimensionsRecord
*/
public int getDimsLoc()
{
log.log(log.DEBUG, "getDimsLoc dimsloc= " + dimsloc);
return dimsloc;
}
/**
* in the event the record is a dimensions record, resets both the loc index and dimsloc index
*/
public void checkDimsLoc(Record rec, int recloc)
{
if (rec.getSid() == DimensionsRecord.sid)
{
loc = recloc;
dimsloc = recloc;
}
}
public int getSize()
{
int retval = 0;
for (int k = 0; k < records.size(); k++)
{
retval += (( Record ) records.get(k)).getRecordSize();
}
return retval;
}
public List getRecords()
{
return records;
}
/**
* Gets the gridset record for this sheet.
*/
public GridsetRecord getGridsetRecord()
{
return gridset;
}
/**
* Returns the first occurance of a record matching a particular sid.
*/
public Record findFirstRecordBySid(short sid)
{
for (Iterator iterator = records.iterator(); iterator.hasNext(); )
{
Record record = ( Record ) iterator.next();
if (record.getSid() == sid)
{
return record;
}
}
return null;
}
/**
* Sets the SCL record or creates it in the correct place if it does not
* already exist.
*
* @param sclRecord 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.add(windowRecordLoc+1, sclRecord);
}
else
{
records.set(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 index = 0;
for (Iterator iterator = records.iterator(); iterator.hasNext(); )
{
Record record = ( Record ) iterator.next();
if (record.getSid() == sid)
{
return index;
}
index++;
}
return -1;
}
/**
* Returns the HeaderRecord.
* @return HeaderRecord for the sheet.
*/
public HeaderRecord getHeader ()
{
return header;
}
/**
* Sets the HeaderRecord.
* @param newHeader The new HeaderRecord for the sheet.
*/
public void setHeader (HeaderRecord newHeader)
{
header = newHeader;
}
/**
* Returns the FooterRecord.
* @return FooterRecord for the sheet.
*/
public FooterRecord getFooter ()
{
return footer;
}
/**
* Sets the FooterRecord.
* @param newFooter The new FooterRecord for the sheet.
*/
public void setFooter (FooterRecord newFooter)
{
footer = newFooter;
}
/**
* Returns the PrintSetupRecord.
* @return PrintSetupRecord for the sheet.
*/
public PrintSetupRecord getPrintSetup ()
{
return printSetup;
}
/**
* Sets the PrintSetupRecord.
* @param newPrintSetup The new PrintSetupRecord for the sheet.
*/
public void setPrintSetup (PrintSetupRecord newPrintSetup)
{
printSetup = newPrintSetup;
}
/**
* Returns the PrintGridlinesRecord.
* @return PrintGridlinesRecord for the sheet.
*/
public PrintGridlinesRecord getPrintGridlines ()
{
return printGridlines;
}
/**
* Sets the PrintGridlinesRecord.
* @param newPrintGridlines The new PrintGridlinesRecord for the sheet.
*/
public void setPrintGridlines (PrintGridlinesRecord newPrintGridlines)
{
printGridlines = newPrintGridlines;
}
/**
* Sets whether the sheet is selected
* @param sel True to select the sheet, false otherwise.
*/
public void setSelected(boolean sel) {
windowTwo.setSelected(sel);
}
/**
* Gets the size of the margin in inches.
* @param margin which margin to get
* @return the size of the margin
*/
public double getMargin(short margin) {
if (getMargins()[margin] != null)
return margins[margin].getMargin();
else {
switch ( margin )
{
case LeftMargin:
return .75;
case RightMargin:
return .75;
case TopMargin:
return 1.0;
case BottomMargin:
return 1.0;
default :
throw new RuntimeException( "Unknown margin constant: " + margin );
}
}
}
/**
* Sets the size of the margin in inches.
* @param margin which margin to get
* @param size the size of the margin
*/
public void setMargin(short margin, double size) {
Margin m = getMargins()[margin];
if (m == null) {
switch ( margin )
{
case LeftMargin:
m = new LeftMarginRecord();
records.add( getDimsLoc() + 1, m );
break;
case RightMargin:
m = new RightMarginRecord();
records.add( getDimsLoc() + 1, m );
break;
case TopMargin:
m = new TopMarginRecord();
records.add( getDimsLoc() + 1, m );
break;
case BottomMargin:
m = new BottomMarginRecord();
records.add( getDimsLoc() + 1, m );
break;
default :
throw new RuntimeException( "Unknown margin constant: " + margin );
}
margins[margin] = m;
}
m.setMargin( size );
}
public int getEofLoc()
{
return eofLoc;
}
/**
* Creates a split (freezepane).
* @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 loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
PaneRecord pane = new PaneRecord();
pane.setX((short)colSplit);
pane.setY((short)rowSplit);
pane.setTopRow((short) topRow);
pane.setLeftColumn((short) leftmostColumn);
if (rowSplit == 0)
{
pane.setTopRow((short)0);
pane.setActivePane((short)1);
}
else if (colSplit == 0)
{
pane.setLeftColumn((short)64);
pane.setActivePane((short)2);
}
else
{
pane.setActivePane((short)0);
}
records.add(loc+1, pane);
windowTwo.setFreezePanes(true);
windowTwo.setFreezePanesNoSplit(true);
SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
// SelectionRecord sel2 = (SelectionRecord) sel.clone();
// SelectionRecord sel3 = (SelectionRecord) sel.clone();
// SelectionRecord sel4 = (SelectionRecord) sel.clone();
// sel.setPane(PANE_LOWER_RIGHT); // 0
// sel3.setPane(PANE_UPPER_RIGHT); // 1
sel.setPane((byte)pane.getActivePane()); // 2
// sel2.setPane(PANE_UPPER_LEFT); // 3
// sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit));
// sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit));
int selLoc = findFirstRecordLocBySid(SelectionRecord.sid);
// sel.setActiveCellCol((short)15);
// sel.setActiveCellRow((short)15);
// sel2.setActiveCellCol((short)0);
// sel2.setActiveCellRow((short)0);
// records.add(selLoc+1,sel2);
// records.add(selLoc+2,sel3);
// records.add(selLoc+3,sel4);
}
/**
* Creates a split pane.
* @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, int activePane )
{
int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
PaneRecord r = new PaneRecord();
r.setX((short)xSplitPos);
r.setY((short)ySplitPos);
r.setTopRow((short) topRow);
r.setLeftColumn((short) leftmostColumn);
r.setActivePane((short) activePane);
records.add(loc+1, r);
windowTwo.setFreezePanes(false);
windowTwo.setFreezePanesNoSplit(false);
SelectionRecord sel = (SelectionRecord) findFirstRecordBySid(SelectionRecord.sid);
// SelectionRecord sel2 = (SelectionRecord) sel.clone();
// SelectionRecord sel3 = (SelectionRecord) sel.clone();
// SelectionRecord sel4 = (SelectionRecord) sel.clone();
sel.setPane(PANE_LOWER_RIGHT); // 0
// sel3.setPane(PANE_UPPER_RIGHT); // 1
// sel4.setPane(PANE_LOWER_LEFT); // 2
// sel2.setPane(PANE_UPPER_LEFT); // 3
// sel4.setActiveCellCol((short)Math.max(sel3.getActiveCellCol(), colSplit));
// sel3.setActiveCellRow((short)Math.max(sel4.getActiveCellRow(), rowSplit));
int selLoc = findFirstRecordLocBySid(SelectionRecord.sid);
// sel.setActiveCellCol((short)15);
// sel.setActiveCellRow((short)15);
// sel2.setActiveCellCol((short)0);
// sel2.setActiveCellRow((short)0);
// records.add(selLoc+1,sel2);
// records.add(selLoc+2,sel3);
// records.add(selLoc+3,sel4);
}
public SelectionRecord getSelection()
{
return selection;
}
public void setSelection( SelectionRecord selection )
{
this.selection = selection;
}
/**
* creates a Protect record with protect set to false.
* @see org.apache.poi.hssf.record.ProtectRecord
* @see org.apache.poi.hssf.record.Record
* @return a ProtectRecord
*/
protected Record createProtect()
{
log.log(log.DEBUG, "create protect record with protection disabled");
ProtectRecord retval = new ProtectRecord();
retval.setProtect(false);
// by default even when we support encryption we won't
return retval;
}
public ProtectRecord getProtect()
{
return protect;
}
/**
* Sets whether the gridlines are shown in a viewer.
* @param show whether to show gridlines or not
*/
public void setDisplayGridlines(boolean show) {
windowTwo.setDisplayGridlines(show);
}
/**
* Returns if gridlines are displayed.
* @return whether gridlines are displayed
*/
public boolean isDisplayGridlines() {
return windowTwo.getDisplayGridlines();
}
/**
* Sets whether the formulas are shown in a viewer.
* @param show whether to show formulas or not
*/
public void setDisplayFormulas(boolean show) {
windowTwo.setDisplayFormulas(show);
}
/**
* Returns if formulas are displayed.
* @return whether formulas are displayed
*/
public boolean isDisplayFormulas() {
return windowTwo.getDisplayFormulas();
}
/**
* Sets whether the RowColHeadings are shown in a viewer.
* @param show whether to show RowColHeadings or not
*/
public void setDisplayRowColHeadings(boolean show) {
windowTwo.setDisplayRowColHeadings(show);
}
/**
* Returns if RowColHeadings are displayed.
* @return whether RowColHeadings are displayed
*/
public boolean isDisplayRowColHeadings() {
return windowTwo.getDisplayRowColHeadings();
}
/**
* Returns the array of margins. If not created, will create.
*
* @return the array of marings.
*/
protected Margin[] getMargins() {
if (margins == null)
margins = new Margin[4];
return margins;
}
}