2002-01-30 21:22:28 -05:00
|
|
|
/* ====================================================================
|
2006-12-22 14:18:16 -05:00
|
|
|
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
|
2004-04-09 09:05:39 -04:00
|
|
|
|
|
|
|
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.
|
|
|
|
==================================================================== */
|
2004-08-23 04:52:54 -04:00
|
|
|
|
2002-01-30 21:22:28 -05:00
|
|
|
package org.apache.poi.hssf.record.aggregates;
|
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.TreeMap;
|
|
|
|
|
2008-08-31 00:45:00 -04:00
|
|
|
import org.apache.poi.hssf.model.RecordStream;
|
|
|
|
import org.apache.poi.hssf.record.ArrayRecord;
|
2008-08-07 19:49:10 -04:00
|
|
|
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
2004-10-06 23:37:16 -04:00
|
|
|
import org.apache.poi.hssf.record.DBCellRecord;
|
2008-08-31 00:45:00 -04:00
|
|
|
import org.apache.poi.hssf.record.FormulaRecord;
|
2008-08-07 19:49:10 -04:00
|
|
|
import org.apache.poi.hssf.record.IndexRecord;
|
2008-08-28 00:27:41 -04:00
|
|
|
import org.apache.poi.hssf.record.MergeCellsRecord;
|
2002-01-30 21:22:28 -05:00
|
|
|
import org.apache.poi.hssf.record.Record;
|
|
|
|
import org.apache.poi.hssf.record.RowRecord;
|
2008-08-31 00:45:00 -04:00
|
|
|
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
|
|
|
import org.apache.poi.hssf.record.TableRecord;
|
2008-08-28 00:27:41 -04:00
|
|
|
import org.apache.poi.hssf.record.UnknownRecord;
|
2008-09-16 16:17:30 -04:00
|
|
|
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
2002-01-30 21:22:28 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @author andy
|
2002-09-04 20:26:28 -04:00
|
|
|
* @author Jason Height (jheight at chariot dot net dot au)
|
2002-01-30 21:22:28 -05:00
|
|
|
*/
|
2008-08-07 21:30:30 -04:00
|
|
|
public final class RowRecordsAggregate extends RecordAggregate {
|
2008-08-07 19:49:10 -04:00
|
|
|
private int _firstrow = -1;
|
|
|
|
private int _lastrow = -1;
|
|
|
|
private final Map _rowRecords;
|
|
|
|
private final ValueRecordsAggregate _valuesAgg;
|
2008-08-28 00:27:41 -04:00
|
|
|
private final List _unknownRecords;
|
2008-08-31 00:45:00 -04:00
|
|
|
private final SharedValueManager _sharedValueManager;
|
2002-01-30 21:22:28 -05:00
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
/** Creates a new instance of ValueRecordsAggregate */
|
2008-08-07 19:49:10 -04:00
|
|
|
public RowRecordsAggregate() {
|
2008-08-31 00:45:00 -04:00
|
|
|
this(SharedValueManager.EMPTY);
|
2008-08-07 19:49:10 -04:00
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
private RowRecordsAggregate(SharedValueManager svm) {
|
|
|
|
_rowRecords = new TreeMap();
|
|
|
|
_valuesAgg = new ValueRecordsAggregate();
|
2008-08-28 00:27:41 -04:00
|
|
|
_unknownRecords = new ArrayList();
|
2008-08-31 00:45:00 -04:00
|
|
|
_sharedValueManager = svm;
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
2008-08-31 00:45:00 -04:00
|
|
|
/**
|
|
|
|
* @param rs record stream with all {@link SharedFormulaRecord}
|
|
|
|
* {@link ArrayRecord}, {@link TableRecord} {@link MergeCellsRecord} Records removed
|
|
|
|
*/
|
|
|
|
public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) {
|
|
|
|
this(svm);
|
|
|
|
while(rs.hasNext()) {
|
|
|
|
Record rec = rs.getNext();
|
2008-08-28 00:27:41 -04:00
|
|
|
switch (rec.getSid()) {
|
|
|
|
case RowRecord.sid:
|
|
|
|
insertRow((RowRecord) rec);
|
|
|
|
continue;
|
|
|
|
case DBCellRecord.sid:
|
|
|
|
// end of 'Row Block'. Should only occur after cell records
|
2008-08-31 00:45:00 -04:00
|
|
|
// ignore DBCELL records because POI generates them upon re-serialization
|
2008-08-28 00:27:41 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (rec instanceof UnknownRecord) {
|
|
|
|
addUnknownRecord((UnknownRecord)rec);
|
|
|
|
// might need to keep track of where exactly these belong
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-29 14:43:53 -04:00
|
|
|
if (!(rec instanceof CellValueRecordInterface)) {
|
2008-08-28 00:27:41 -04:00
|
|
|
throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")");
|
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
_valuesAgg.construct((CellValueRecordInterface)rec, rs, svm);
|
2008-08-28 00:27:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Handles UnknownRecords which appear within the row/cell records
|
|
|
|
*/
|
|
|
|
private void addUnknownRecord(UnknownRecord rec) {
|
|
|
|
// ony a few distinct record IDs are encountered by the existing POI test cases:
|
|
|
|
// 0x1065 // many
|
|
|
|
// 0x01C2 // several
|
|
|
|
// 0x0034 // few
|
|
|
|
// No documentation could be found for these
|
2008-08-31 00:45:00 -04:00
|
|
|
|
2008-08-28 00:27:41 -04:00
|
|
|
// keep the unknown records for re-serialization
|
|
|
|
_unknownRecords.add(rec);
|
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
public void insertRow(RowRecord row) {
|
2002-01-30 21:22:28 -05:00
|
|
|
// Integer integer = new Integer(row.getRowNumber());
|
2008-08-07 19:49:10 -04:00
|
|
|
_rowRecords.put(new Integer(row.getRowNumber()), row);
|
|
|
|
if ((row.getRowNumber() < _firstrow) || (_firstrow == -1))
|
2002-01-30 21:22:28 -05:00
|
|
|
{
|
2008-08-07 19:49:10 -04:00
|
|
|
_firstrow = row.getRowNumber();
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
if ((row.getRowNumber() > _lastrow) || (_lastrow == -1))
|
2002-01-30 21:22:28 -05:00
|
|
|
{
|
2008-08-07 19:49:10 -04:00
|
|
|
_lastrow = row.getRowNumber();
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
public void removeRow(RowRecord row) {
|
|
|
|
int rowIndex = row.getRowNumber();
|
|
|
|
_valuesAgg.removeAllCellsValuesForRow(rowIndex);
|
|
|
|
Integer key = new Integer(rowIndex);
|
|
|
|
RowRecord rr = (RowRecord) _rowRecords.remove(key);
|
|
|
|
if (rr == null) {
|
|
|
|
throw new RuntimeException("Invalid row index (" + key.intValue() + ")");
|
|
|
|
}
|
|
|
|
if (row != rr) {
|
|
|
|
_rowRecords.put(key, rr);
|
|
|
|
throw new RuntimeException("Attempt to remove row that does not belong to this sheet");
|
|
|
|
}
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
public RowRecord getRow(int rowIndex) {
|
|
|
|
if (rowIndex < 0 || rowIndex > 65535) {
|
2008-05-11 04:15:39 -04:00
|
|
|
throw new IllegalArgumentException("The row number must be between 0 and 65535");
|
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
return (RowRecord) _rowRecords.get(new Integer(rowIndex));
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getPhysicalNumberOfRows()
|
|
|
|
{
|
2008-08-07 19:49:10 -04:00
|
|
|
return _rowRecords.size();
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getFirstRowNum()
|
|
|
|
{
|
2008-08-07 19:49:10 -04:00
|
|
|
return _firstrow;
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getLastRowNum()
|
|
|
|
{
|
2008-08-07 19:49:10 -04:00
|
|
|
return _lastrow;
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
|
2004-10-06 23:37:16 -04:00
|
|
|
/** Returns the number of row blocks.
|
|
|
|
* <p/>The row blocks are goupings of rows that contain the DBCell record
|
|
|
|
* after them
|
|
|
|
*/
|
|
|
|
public int getRowBlockCount() {
|
2008-08-07 19:49:10 -04:00
|
|
|
int size = _rowRecords.size()/DBCellRecord.BLOCK_SIZE;
|
|
|
|
if ((_rowRecords.size() % DBCellRecord.BLOCK_SIZE) != 0)
|
2004-10-06 23:37:16 -04:00
|
|
|
size++;
|
|
|
|
return size;
|
|
|
|
}
|
2002-01-30 21:22:28 -05:00
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
private int getRowBlockSize(int block) {
|
|
|
|
return RowRecord.ENCODED_SIZE * getRowCountForBlock(block);
|
2004-10-06 23:37:16 -04:00
|
|
|
}
|
2004-08-23 04:52:54 -04:00
|
|
|
|
2004-10-06 23:37:16 -04:00
|
|
|
/** Returns the number of physical rows within a block*/
|
|
|
|
public int getRowCountForBlock(int block) {
|
|
|
|
int startIndex = block * DBCellRecord.BLOCK_SIZE;
|
|
|
|
int endIndex = startIndex + DBCellRecord.BLOCK_SIZE - 1;
|
2008-08-07 19:49:10 -04:00
|
|
|
if (endIndex >= _rowRecords.size())
|
|
|
|
endIndex = _rowRecords.size()-1;
|
2002-01-30 21:22:28 -05:00
|
|
|
|
2004-10-06 23:37:16 -04:00
|
|
|
return endIndex-startIndex+1;
|
|
|
|
}
|
2002-01-30 21:22:28 -05:00
|
|
|
|
2004-10-06 23:37:16 -04:00
|
|
|
/** Returns the physical row number of the first row in a block*/
|
2008-08-07 19:49:10 -04:00
|
|
|
private int getStartRowNumberForBlock(int block) {
|
2005-08-18 03:06:44 -04:00
|
|
|
//Given that we basically iterate through the rows in order,
|
2008-08-07 19:49:10 -04:00
|
|
|
// TODO - For a performance improvement, it would be better to return an instance of
|
2004-10-06 23:37:16 -04:00
|
|
|
//an iterator and use that instance throughout, rather than recreating one and
|
|
|
|
//having to move it to the right position.
|
|
|
|
int startIndex = block * DBCellRecord.BLOCK_SIZE;
|
2008-08-07 19:49:10 -04:00
|
|
|
Iterator rowIter = _rowRecords.values().iterator();
|
2004-10-06 23:37:16 -04:00
|
|
|
RowRecord row = null;
|
|
|
|
//Position the iterator at the start of the block
|
|
|
|
for (int i=0; i<=startIndex;i++) {
|
|
|
|
row = (RowRecord)rowIter.next();
|
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
if (row == null) {
|
|
|
|
throw new RuntimeException("Did not find start row for block " + block);
|
|
|
|
}
|
2004-10-06 23:37:16 -04:00
|
|
|
|
|
|
|
return row.getRowNumber();
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
2004-10-06 23:37:16 -04:00
|
|
|
|
|
|
|
/** Returns the physical row number of the end row in a block*/
|
2008-08-07 19:49:10 -04:00
|
|
|
private int getEndRowNumberForBlock(int block) {
|
2004-10-06 23:37:16 -04:00
|
|
|
int endIndex = ((block + 1)*DBCellRecord.BLOCK_SIZE)-1;
|
2008-08-07 19:49:10 -04:00
|
|
|
if (endIndex >= _rowRecords.size())
|
|
|
|
endIndex = _rowRecords.size()-1;
|
2004-10-06 23:37:16 -04:00
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
Iterator rowIter = _rowRecords.values().iterator();
|
2004-10-06 23:37:16 -04:00
|
|
|
RowRecord row = null;
|
|
|
|
for (int i=0; i<=endIndex;i++) {
|
|
|
|
row = (RowRecord)rowIter.next();
|
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
if (row == null) {
|
|
|
|
throw new RuntimeException("Did not find start row for block " + block);
|
|
|
|
}
|
2004-10-06 23:37:16 -04:00
|
|
|
return row.getRowNumber();
|
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
|
2008-08-07 21:30:30 -04:00
|
|
|
private int visitRowRecordsForBlock(int blockIndex, RecordVisitor rv) {
|
|
|
|
final int startIndex = blockIndex*DBCellRecord.BLOCK_SIZE;
|
|
|
|
final int endIndex = startIndex + DBCellRecord.BLOCK_SIZE;
|
|
|
|
|
|
|
|
Iterator rowIterator = _rowRecords.values().iterator();
|
|
|
|
|
|
|
|
//Given that we basically iterate through the rows in order,
|
|
|
|
//For a performance improvement, it would be better to return an instance of
|
|
|
|
//an iterator and use that instance throughout, rather than recreating one and
|
|
|
|
//having to move it to the right position.
|
|
|
|
int i=0;
|
|
|
|
for (;i<startIndex;i++)
|
|
|
|
rowIterator.next();
|
|
|
|
int result = 0;
|
|
|
|
while(rowIterator.hasNext() && (i++ < endIndex)) {
|
|
|
|
Record rec = (Record)rowIterator.next();
|
|
|
|
result += rec.getRecordSize();
|
|
|
|
rv.visitRecord(rec);
|
|
|
|
}
|
|
|
|
return result;
|
2008-08-31 00:45:00 -04:00
|
|
|
}
|
|
|
|
|
2008-08-07 21:30:30 -04:00
|
|
|
public void visitContainedRecords(RecordVisitor rv) {
|
2008-08-31 00:45:00 -04:00
|
|
|
|
|
|
|
PositionTrackingVisitor stv = new PositionTrackingVisitor(rv, 0);
|
2004-10-06 23:37:16 -04:00
|
|
|
//DBCells are serialized before row records.
|
|
|
|
final int blockCount = getRowBlockCount();
|
2008-08-07 21:30:30 -04:00
|
|
|
for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) {
|
|
|
|
// Serialize a block of rows.
|
|
|
|
// Hold onto the position of the first row in the block
|
|
|
|
int pos=0;
|
|
|
|
// Hold onto the size of this block that was serialized
|
|
|
|
final int rowBlockSize = visitRowRecordsForBlock(blockIndex, rv);
|
|
|
|
pos += rowBlockSize;
|
|
|
|
// Serialize a block of cells for those rows
|
|
|
|
final int startRowNumber = getStartRowNumberForBlock(blockIndex);
|
|
|
|
final int endRowNumber = getEndRowNumberForBlock(blockIndex);
|
2008-11-18 19:06:06 -05:00
|
|
|
DBCellRecord.Builder dbcrBuilder = new DBCellRecord.Builder();
|
2008-08-07 21:30:30 -04:00
|
|
|
// Note: Cell references start from the second row...
|
|
|
|
int cellRefOffset = (rowBlockSize - RowRecord.ENCODED_SIZE);
|
|
|
|
for (int row = startRowNumber; row <= endRowNumber; row++) {
|
2008-08-31 00:45:00 -04:00
|
|
|
if (_valuesAgg.rowHasCells(row)) {
|
|
|
|
stv.setPosition(0);
|
|
|
|
_valuesAgg.visitCellsForRow(row, stv);
|
|
|
|
int rowCellSize = stv.getPosition();
|
2008-08-07 21:30:30 -04:00
|
|
|
pos += rowCellSize;
|
|
|
|
// Add the offset to the first cell for the row into the
|
|
|
|
// DBCellRecord.
|
2008-11-18 19:06:06 -05:00
|
|
|
dbcrBuilder.addCellOffset(cellRefOffset);
|
2008-08-07 21:30:30 -04:00
|
|
|
cellRefOffset = rowCellSize;
|
|
|
|
}
|
2004-10-06 23:37:16 -04:00
|
|
|
}
|
2008-08-07 21:30:30 -04:00
|
|
|
// Calculate Offset from the start of a DBCellRecord to the first Row
|
2008-11-18 19:06:06 -05:00
|
|
|
rv.visitRecord(dbcrBuilder.build(pos));
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
2008-08-28 00:27:41 -04:00
|
|
|
for (int i=0; i< _unknownRecords.size(); i++) {
|
|
|
|
// Potentially breaking the file here since we don't know exactly where to write these records
|
|
|
|
rv.visitRecord((Record) _unknownRecords.get(i));
|
|
|
|
}
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
2008-08-07 21:30:30 -04:00
|
|
|
public Iterator getIterator() {
|
2008-08-07 19:49:10 -04:00
|
|
|
return _rowRecords.values().iterator();
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
public Iterator getAllRecordsIterator() {
|
|
|
|
List result = new ArrayList(_rowRecords.size() * 2);
|
|
|
|
result.addAll(_rowRecords.values());
|
|
|
|
Iterator vi = _valuesAgg.getIterator();
|
|
|
|
while (vi.hasNext()) {
|
|
|
|
result.add(vi.next());
|
|
|
|
}
|
|
|
|
return result.iterator();
|
|
|
|
}
|
2004-08-23 04:52:54 -04:00
|
|
|
|
|
|
|
public int findStartOfRowOutlineGroup(int row)
|
|
|
|
{
|
|
|
|
// Find the start of the group.
|
|
|
|
RowRecord rowRecord = this.getRow( row );
|
|
|
|
int level = rowRecord.getOutlineLevel();
|
|
|
|
int currentRow = row;
|
|
|
|
while (this.getRow( currentRow ) != null)
|
|
|
|
{
|
|
|
|
rowRecord = this.getRow( currentRow );
|
|
|
|
if (rowRecord.getOutlineLevel() < level)
|
|
|
|
return currentRow + 1;
|
|
|
|
currentRow--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentRow + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int findEndOfRowOutlineGroup( int row )
|
2003-07-18 22:48:17 -04:00
|
|
|
{
|
2004-08-23 04:52:54 -04:00
|
|
|
int level = getRow( row ).getOutlineLevel();
|
|
|
|
int currentRow;
|
|
|
|
for (currentRow = row; currentRow < this.getLastRowNum(); currentRow++)
|
|
|
|
{
|
|
|
|
if (getRow(currentRow) == null || getRow(currentRow).getOutlineLevel() < level)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-07-18 22:48:17 -04:00
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
return currentRow-1;
|
|
|
|
}
|
|
|
|
|
2008-10-30 13:56:34 -04:00
|
|
|
/**
|
|
|
|
* Hide all rows at or below the current outline level
|
|
|
|
* @return index of the <em>next<em> row after the last row that gets hidden
|
|
|
|
*/
|
|
|
|
private int writeHidden(RowRecord pRowRecord, int row) {
|
|
|
|
int rowIx = row;
|
|
|
|
RowRecord rowRecord = pRowRecord;
|
2004-08-23 04:52:54 -04:00
|
|
|
int level = rowRecord.getOutlineLevel();
|
2008-10-30 13:56:34 -04:00
|
|
|
while (rowRecord != null && getRow(rowIx).getOutlineLevel() >= level) {
|
|
|
|
rowRecord.setZeroHeight(true);
|
|
|
|
rowIx++;
|
|
|
|
rowRecord = getRow(rowIx);
|
2004-08-23 04:52:54 -04:00
|
|
|
}
|
2008-10-30 13:56:34 -04:00
|
|
|
return rowIx;
|
2004-08-23 04:52:54 -04:00
|
|
|
}
|
|
|
|
|
2008-10-30 13:56:34 -04:00
|
|
|
public void collapseRow(int rowNumber) {
|
2004-08-23 04:52:54 -04:00
|
|
|
|
|
|
|
// Find the start of the group.
|
2008-10-30 13:56:34 -04:00
|
|
|
int startRow = findStartOfRowOutlineGroup(rowNumber);
|
|
|
|
RowRecord rowRecord = getRow(startRow);
|
2004-08-23 04:52:54 -04:00
|
|
|
|
|
|
|
// Hide all the columns until the end of the group
|
2008-10-30 13:56:34 -04:00
|
|
|
int nextRowIx = writeHidden(rowRecord, startRow);
|
2004-08-23 04:52:54 -04:00
|
|
|
|
2008-10-30 13:56:34 -04:00
|
|
|
RowRecord row = getRow(nextRowIx);
|
|
|
|
if (row == null) {
|
|
|
|
row = createRow(nextRowIx);
|
|
|
|
insertRow(row);
|
2003-07-18 22:48:17 -04:00
|
|
|
}
|
2008-10-30 13:56:34 -04:00
|
|
|
// Write collapse field
|
|
|
|
row.setColapsed(true);
|
2003-07-18 22:48:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2004-08-23 04:52:54 -04:00
|
|
|
* Create a row record.
|
|
|
|
*
|
2008-11-07 11:57:23 -05:00
|
|
|
* @param rowNumber row number
|
2004-08-23 04:52:54 -04:00
|
|
|
* @return RowRecord created for the passed in row number
|
|
|
|
* @see org.apache.poi.hssf.record.RowRecord
|
2003-07-18 22:48:17 -04:00
|
|
|
*/
|
2008-05-11 04:15:39 -04:00
|
|
|
public static RowRecord createRow(int rowNumber) {
|
|
|
|
return new RowRecord(rowNumber);
|
2004-08-23 04:52:54 -04:00
|
|
|
}
|
2003-07-18 22:48:17 -04:00
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
public boolean isRowGroupCollapsed( int row )
|
2003-07-18 22:48:17 -04:00
|
|
|
{
|
2004-08-23 04:52:54 -04:00
|
|
|
int collapseRow = findEndOfRowOutlineGroup( row ) + 1;
|
|
|
|
|
|
|
|
if (getRow(collapseRow) == null)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return getRow( collapseRow ).getColapsed();
|
|
|
|
}
|
2003-07-18 22:48:17 -04:00
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
public void expandRow( int rowNumber )
|
|
|
|
{
|
|
|
|
int idx = rowNumber;
|
|
|
|
if (idx == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If it is already expanded do nothing.
|
|
|
|
if (!isRowGroupCollapsed(idx))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Find the start of the group.
|
|
|
|
int startIdx = findStartOfRowOutlineGroup( idx );
|
|
|
|
RowRecord row = getRow( startIdx );
|
|
|
|
|
|
|
|
// Find the end of the group.
|
|
|
|
int endIdx = findEndOfRowOutlineGroup( idx );
|
|
|
|
|
|
|
|
// expand:
|
2008-05-11 04:15:39 -04:00
|
|
|
// collapsed bit must be unset
|
2004-08-23 04:52:54 -04:00
|
|
|
// hidden bit gets unset _if_ surrounding groups are expanded you can determine
|
|
|
|
// this by looking at the hidden bit of the enclosing group. You will have
|
|
|
|
// to look at the start and the end of the current group to determine which
|
|
|
|
// is the enclosing group
|
2008-05-11 04:15:39 -04:00
|
|
|
// hidden bit only is altered for this outline level. ie. don't un-collapse contained groups
|
2004-08-23 04:52:54 -04:00
|
|
|
if ( !isRowGroupHiddenByParent( idx ) )
|
2003-07-18 22:48:17 -04:00
|
|
|
{
|
2004-08-23 04:52:54 -04:00
|
|
|
for ( int i = startIdx; i <= endIdx; i++ )
|
|
|
|
{
|
|
|
|
if ( row.getOutlineLevel() == getRow( i ).getOutlineLevel() )
|
|
|
|
getRow( i ).setZeroHeight( false );
|
|
|
|
else if (!isRowGroupCollapsed(i))
|
|
|
|
getRow( i ).setZeroHeight( false );
|
|
|
|
}
|
2003-07-18 22:48:17 -04:00
|
|
|
}
|
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
// Write collapse field
|
|
|
|
getRow( endIdx + 1 ).setColapsed( false );
|
2003-07-18 22:48:17 -04:00
|
|
|
}
|
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
public boolean isRowGroupHiddenByParent( int row )
|
|
|
|
{
|
|
|
|
// Look out outline details of end
|
|
|
|
int endLevel;
|
|
|
|
boolean endHidden;
|
|
|
|
int endOfOutlineGroupIdx = findEndOfRowOutlineGroup( row );
|
|
|
|
if (getRow( endOfOutlineGroupIdx + 1 ) == null)
|
|
|
|
{
|
|
|
|
endLevel = 0;
|
|
|
|
endHidden = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
endLevel = getRow( endOfOutlineGroupIdx + 1).getOutlineLevel();
|
|
|
|
endHidden = getRow( endOfOutlineGroupIdx + 1).getZeroHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look out outline details of start
|
|
|
|
int startLevel;
|
|
|
|
boolean startHidden;
|
|
|
|
int startOfOutlineGroupIdx = findStartOfRowOutlineGroup( row );
|
|
|
|
if (startOfOutlineGroupIdx - 1 < 0 || getRow(startOfOutlineGroupIdx - 1) == null)
|
|
|
|
{
|
|
|
|
startLevel = 0;
|
|
|
|
startHidden = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
startLevel = getRow( startOfOutlineGroupIdx - 1).getOutlineLevel();
|
|
|
|
startHidden = getRow( startOfOutlineGroupIdx - 1 ).getZeroHeight();
|
|
|
|
}
|
2003-07-18 22:48:17 -04:00
|
|
|
|
2004-08-23 04:52:54 -04:00
|
|
|
if (endLevel > startLevel)
|
|
|
|
{
|
|
|
|
return endHidden;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return startHidden;
|
|
|
|
}
|
2002-09-04 20:26:28 -04:00
|
|
|
}
|
|
|
|
|
2008-08-07 19:49:10 -04:00
|
|
|
public CellValueRecordInterface[] getValueRecords() {
|
|
|
|
return _valuesAgg.getValueRecords();
|
|
|
|
}
|
|
|
|
|
|
|
|
public IndexRecord createIndexRecord(int indexRecordOffset, int sizeOfInitialSheetRecords) {
|
|
|
|
IndexRecord result = new IndexRecord();
|
|
|
|
result.setFirstRow(_firstrow);
|
|
|
|
result.setLastRowAdd1(_lastrow + 1);
|
|
|
|
// Calculate the size of the records from the end of the BOF
|
|
|
|
// and up to the RowRecordsAggregate...
|
|
|
|
|
|
|
|
// Add the references to the DBCells in the IndexRecord (one for each block)
|
|
|
|
// Note: The offsets are relative to the Workbook BOF. Assume that this is
|
|
|
|
// 0 for now.....
|
|
|
|
|
|
|
|
int blockCount = getRowBlockCount();
|
|
|
|
// Calculate the size of this IndexRecord
|
|
|
|
int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount);
|
|
|
|
|
|
|
|
int currentOffset = indexRecordOffset + indexRecSize + sizeOfInitialSheetRecords;
|
|
|
|
|
|
|
|
for (int block = 0; block < blockCount; block++) {
|
|
|
|
// each row-block has a DBCELL record.
|
|
|
|
// The offset of each DBCELL record needs to be updated in the INDEX record
|
|
|
|
|
|
|
|
// account for row records in this row-block
|
|
|
|
currentOffset += getRowBlockSize(block);
|
|
|
|
// account for cell value records after those
|
|
|
|
currentOffset += _valuesAgg.getRowCellBlockSize(
|
|
|
|
getStartRowNumberForBlock(block), getEndRowNumberForBlock(block));
|
|
|
|
|
|
|
|
// currentOffset is now the location of the DBCELL record for this row-block
|
|
|
|
result.addDbcell(currentOffset);
|
|
|
|
// Add space required to write the DBCELL record (whose reference was just added).
|
|
|
|
currentOffset += (8 + (getRowCountForBlock(block) * 2));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
public void insertCell(CellValueRecordInterface cvRec) {
|
|
|
|
_valuesAgg.insertCell(cvRec);
|
|
|
|
}
|
|
|
|
public void removeCell(CellValueRecordInterface cvRec) {
|
2008-10-30 16:17:04 -04:00
|
|
|
if (cvRec instanceof FormulaRecordAggregate) {
|
|
|
|
((FormulaRecordAggregate)cvRec).notifyFormulaChanging();
|
|
|
|
}
|
2008-08-07 19:49:10 -04:00
|
|
|
_valuesAgg.removeCell(cvRec);
|
|
|
|
}
|
2008-08-31 00:45:00 -04:00
|
|
|
public FormulaRecordAggregate createFormula(int row, int col) {
|
|
|
|
FormulaRecord fr = new FormulaRecord();
|
|
|
|
fr.setRow(row);
|
|
|
|
fr.setColumn((short) col);
|
|
|
|
return new FormulaRecordAggregate(fr, null, _sharedValueManager);
|
|
|
|
}
|
2008-09-16 16:17:30 -04:00
|
|
|
public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
|
|
|
|
_valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
|
|
|
|
}
|
2002-01-30 21:22:28 -05:00
|
|
|
}
|