Fix for bug 46280 - RowRecordsAggregate should allow for ContinueRecords after UnkownRecords, and PivotTable records should not get in the RowRecordsAggregate at all

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@720318 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-11-24 22:40:46 +00:00
parent 1559edee21
commit 7735bb1b3b
9 changed files with 529 additions and 440 deletions

View File

@ -37,7 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.5-beta5" date="2008-??-??"> <release version="3.5-beta5" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix"><!--remove me--></action> <action dev="POI-DEVELOPERS" type="fix">46280 - Fixed RowRecordsAggregate etc to properly skip PivotTable records</action>
</release> </release>
<release version="3.5-beta4" date="2008-11-29"> <release version="3.5-beta4" date="2008-11-29">
<action dev="POI-DEVELOPERS" type="fix">46213 - Fixed FormulaRecordAggregate to gracefully ignore extra StringRecords</action> <action dev="POI-DEVELOPERS" type="fix">46213 - Fixed FormulaRecordAggregate to gracefully ignore extra StringRecords</action>

View File

@ -34,7 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.5-beta5" date="2008-??-??"> <release version="3.5-beta5" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix"><!--remove me--></action> <action dev="POI-DEVELOPERS" type="fix">46280 - Fixed RowRecordsAggregate etc to properly skip PivotTable records</action>
</release> </release>
<release version="3.5-beta4" date="2008-11-29"> <release version="3.5-beta4" date="2008-11-29">
<action dev="POI-DEVELOPERS" type="fix">46213 - Fixed FormulaRecordAggregate to gracefully ignore extra StringRecords</action> <action dev="POI-DEVELOPERS" type="fix">46213 - Fixed FormulaRecordAggregate to gracefully ignore extra StringRecords</action>

View File

@ -85,12 +85,12 @@ final class RecordOrderer {
* Adds the specified new record in the correct place in sheet records list * Adds the specified new record in the correct place in sheet records list
* *
*/ */
public static void addNewSheetRecord(List sheetRecords, RecordBase newRecord) { public static void addNewSheetRecord(List<RecordBase> sheetRecords, RecordBase newRecord) {
int index = findSheetInsertPos(sheetRecords, newRecord.getClass()); int index = findSheetInsertPos(sheetRecords, newRecord.getClass());
sheetRecords.add(index, newRecord); sheetRecords.add(index, newRecord);
} }
private static int findSheetInsertPos(List records, Class recClass) { private static int findSheetInsertPos(List<RecordBase> records, Class recClass) {
if (recClass == DataValidityTable.class) { if (recClass == DataValidityTable.class) {
return findDataValidationTableInsertPos(records); return findDataValidationTableInsertPos(records);
} }
@ -109,7 +109,7 @@ final class RecordOrderer {
throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")"); throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")");
} }
private static int getPageBreakRecordInsertPos(List records) { private static int getPageBreakRecordInsertPos(List<RecordBase> records) {
int dimensionsIndex = getDimensionsIndex(records); int dimensionsIndex = getDimensionsIndex(records);
int i = dimensionsIndex-1; int i = dimensionsIndex-1;
while (i > 0) { while (i > 0) {
@ -152,7 +152,7 @@ final class RecordOrderer {
/** /**
* Find correct position to add new CFHeader record * Find correct position to add new CFHeader record
*/ */
private static int findInsertPosForNewCondFormatTable(List records) { private static int findInsertPosForNewCondFormatTable(List<RecordBase> records) {
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
Object rb = records.get(i); Object rb = records.get(i);
@ -175,7 +175,7 @@ final class RecordOrderer {
throw new RuntimeException("Did not find Window2 record"); throw new RuntimeException("Did not find Window2 record");
} }
private static int findInsertPosForNewMergedRecordTable(List records) { private static int findInsertPosForNewMergedRecordTable(List<RecordBase> records) {
for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record
Object rb = records.get(i); Object rb = records.get(i);
if (!(rb instanceof Record)) { if (!(rb instanceof Record)) {
@ -219,14 +219,14 @@ final class RecordOrderer {
* o RANGEPROTECTION * o RANGEPROTECTION
* + EOF * + EOF
*/ */
private static int findDataValidationTableInsertPos(List records) { private static int findDataValidationTableInsertPos(List<RecordBase> records) {
int i = records.size() - 1; int i = records.size() - 1;
if (!(records.get(i) instanceof EOFRecord)) { if (!(records.get(i) instanceof EOFRecord)) {
throw new IllegalStateException("Last sheet record should be EOFRecord"); throw new IllegalStateException("Last sheet record should be EOFRecord");
} }
while (i > 0) { while (i > 0) {
i--; i--;
Object rb = records.get(i); RecordBase rb = records.get(i);
if (isDVTPriorRecord(rb)) { if (isDVTPriorRecord(rb)) {
Record nextRec = (Record) records.get(i + 1); Record nextRec = (Record) records.get(i + 1);
if (!isDVTSubsequentRecord(nextRec.getSid())) { if (!isDVTSubsequentRecord(nextRec.getSid())) {
@ -245,7 +245,7 @@ final class RecordOrderer {
} }
private static boolean isDVTPriorRecord(Object rb) { private static boolean isDVTPriorRecord(RecordBase rb) {
if (rb instanceof MergedCellsTable || rb instanceof ConditionalFormattingTable) { if (rb instanceof MergedCellsTable || rb instanceof ConditionalFormattingTable) {
return true; return true;
} }
@ -280,7 +280,7 @@ final class RecordOrderer {
/** /**
* DIMENSIONS record is always present * DIMENSIONS record is always present
*/ */
private static int getDimensionsIndex(List records) { private static int getDimensionsIndex(List<RecordBase> records) {
int nRecs = records.size(); int nRecs = records.size();
for(int i=0; i<nRecs; i++) { for(int i=0; i<nRecs; i++) {
if(records.get(i) instanceof DimensionsRecord) { if(records.get(i) instanceof DimensionsRecord) {
@ -291,12 +291,12 @@ final class RecordOrderer {
throw new RuntimeException("DimensionsRecord not found"); throw new RuntimeException("DimensionsRecord not found");
} }
private static int getGutsRecordInsertPos(List records) { private static int getGutsRecordInsertPos(List<RecordBase> records) {
int dimensionsIndex = getDimensionsIndex(records); int dimensionsIndex = getDimensionsIndex(records);
int i = dimensionsIndex-1; int i = dimensionsIndex-1;
while (i > 0) { while (i > 0) {
i--; i--;
Object rb = records.get(i); RecordBase rb = records.get(i);
if (isGutsPriorRecord(rb)) { if (isGutsPriorRecord(rb)) {
return i+1; return i+1;
} }
@ -304,7 +304,7 @@ final class RecordOrderer {
throw new RuntimeException("Did not find insert point for GUTS"); throw new RuntimeException("Did not find insert point for GUTS");
} }
private static boolean isGutsPriorRecord(Object rb) { private static boolean isGutsPriorRecord(RecordBase rb) {
if (rb instanceof Record) { if (rb instanceof Record) {
Record record = (Record) rb; Record record = (Record) rb;
switch (record.getSid()) { switch (record.getSid()) {
@ -337,6 +337,8 @@ final class RecordOrderer {
*/ */
public static boolean isEndOfRowBlock(int sid) { public static boolean isEndOfRowBlock(int sid) {
switch(sid) { switch(sid) {
case UnknownRecord.SXVIEW_00B0:
// should have been prefixed with DrawingRecord (0x00EC), but bug 46280 seems to allow this
case DrawingRecord.sid: case DrawingRecord.sid:
case DrawingSelectionRecord.sid: case DrawingSelectionRecord.sid:
case ObjRecord.sid: case ObjRecord.sid:

View File

@ -45,11 +45,11 @@ public final class RowBlocksReader {
* mergedCellsTable * mergedCellsTable
*/ */
public RowBlocksReader(RecordStream rs) { public RowBlocksReader(RecordStream rs) {
List plainRecords = new ArrayList(); List<Record> plainRecords = new ArrayList<Record>();
List shFrmRecords = new ArrayList(); List<Record> shFrmRecords = new ArrayList<Record>();
List arrayRecords = new ArrayList(); List<Record> arrayRecords = new ArrayList<Record>();
List tableRecords = new ArrayList(); List<Record> tableRecords = new ArrayList<Record>();
List mergeCellRecords = new ArrayList(); List<Record> mergeCellRecords = new ArrayList<Record>();
while(!RecordOrderer.isEndOfRowBlock(rs.peekNextSid())) { while(!RecordOrderer.isEndOfRowBlock(rs.peekNextSid())) {
// End of row/cell records for the current sheet // End of row/cell records for the current sheet
@ -61,7 +61,7 @@ public final class RowBlocksReader {
} }
Record rec = rs.getNext(); Record rec = rs.getNext();
List dest; List<Record> dest;
switch (rec.getSid()) { switch (rec.getSid()) {
case MergeCellsRecord.sid: dest = mergeCellRecords; break; case MergeCellsRecord.sid: dest = mergeCellRecords; break;
case SharedFormulaRecord.sid: dest = shFrmRecords; break; case SharedFormulaRecord.sid: dest = shFrmRecords; break;

View File

@ -39,6 +39,7 @@ public final class UnknownRecord extends StandardRecord {
public static final int SHEETPR_0081 = 0x0081; public static final int SHEETPR_0081 = 0x0081;
public static final int STANDARDWIDTH_0099 = 0x0099; public static final int STANDARDWIDTH_0099 = 0x0099;
public static final int SCL_00A0 = 0x00A0; public static final int SCL_00A0 = 0x00A0;
public static final int SXVIEW_00B0 = 0x00B0;
public static final int BITMAP_00E9 = 0x00E9; public static final int BITMAP_00E9 = 0x00E9;
public static final int PHONETICPR_00EF = 0x00EF; public static final int PHONETICPR_00EF = 0x00EF;
public static final int LABELRANGES_015F = 0x015F; public static final int LABELRANGES_015F = 0x015F;
@ -128,13 +129,22 @@ public final class UnknownRecord extends StandardRecord {
case 0x0094: return "LHRECORD"; case 0x0094: return "LHRECORD";
case STANDARDWIDTH_0099: return "STANDARDWIDTH"; case STANDARDWIDTH_0099: return "STANDARDWIDTH";
case 0x009D: return "AUTOFILTERINFO"; case 0x009D: return "AUTOFILTERINFO";
case SCL_00A0: return "SCL"; case SCL_00A0: return "SCL";
case 0x00AE: return "SCENMAN"; case 0x00AE: return "SCENMAN";
case SXVIEW_00B0: return "SXVIEW"; // (pivot table) View Definition
case 0x00B1: return "SXVD"; // (pivot table) View Fields
case 0x00B2: return "SXVI"; // (pivot table) View Item
case 0x00B4: return "SXIVD"; // (pivot table) Row/Column Field IDs
case 0x00B5: return "SXLI"; // (pivot table) Line Item Array
case 0x00C5: return "SXDI"; // (pivot table) Data Item
case 0x00D3: return "OBPROJ"; case 0x00D3: return "OBPROJ";
case 0x00DC: return "PARAMQRY"; case 0x00DC: return "PARAMQRY";
case 0x00DE: return "OLESIZE"; case 0x00DE: return "OLESIZE";
case BITMAP_00E9: return "BITMAP"; case BITMAP_00E9: return "BITMAP";
case PHONETICPR_00EF: return "PHONETICPR"; case PHONETICPR_00EF: return "PHONETICPR";
case 0x00F1: return "SXEX"; // PivotTable View Extended Information
case 0x0100: return "SXVDEX"; // Extended PivotTable View Fields
case LABELRANGES_015F: return "LABELRANGES"; case LABELRANGES_015F: return "LABELRANGES";
case 0x01BA: return "CODENAME"; case 0x01BA: return "CODENAME";
@ -145,14 +155,16 @@ public final class UnknownRecord extends StandardRecord {
case 0x01C0: return "EXCEL9FILE"; case 0x01C0: return "EXCEL9FILE";
case 0x0802: return "QSISXTAG"; case 0x0802: return "QSISXTAG"; // Pivot Table and Query Table Extensions
case 0x0803: return "DBQUERYEXT"; case 0x0803: return "DBQUERYEXT";
case 0x0805: return "TXTQUERY"; case 0x0805: return "TXTQUERY";
case 0x0810: return "SXVIEWEX9"; // Pivot Table Extensions
case 0x0812: return "CONTINUEFRT"; case 0x0812: return "CONTINUEFRT";
case QUICKTIP_0800: return "QUICKTIP"; case QUICKTIP_0800: return "QUICKTIP";
case SHEETEXT_0862: return "SHEETEXT"; case SHEETEXT_0862: return "SHEETEXT";
case 0x0863: return "BOOKEXT"; case 0x0863: return "BOOKEXT";
case 0x0864: return "SXADDL"; // Pivot Table Additional Info
case SHEETPROTECTION_0867: return "SHEETPROTECTION"; case SHEETPROTECTION_0867: return "SHEETPROTECTION";
case RANGEPROTECTION_0868: return "RANGEPROTECTION"; case RANGEPROTECTION_0868: return "RANGEPROTECTION";
case 0x086B: return "DATALABEXTCONTENTS"; case 0x086B: return "DATALABEXTCONTENTS";

View File

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

View File

@ -35,6 +35,7 @@ public final class AllModelTests {
result.addTestSuite(TestFormulaParserEval.class); result.addTestSuite(TestFormulaParserEval.class);
result.addTestSuite(TestFormulaParserIf.class); result.addTestSuite(TestFormulaParserIf.class);
result.addTestSuite(TestOperandClassTransformer.class); result.addTestSuite(TestOperandClassTransformer.class);
result.addTestSuite(TestRowBlocksReader.class);
result.addTestSuite(TestRVA.class); result.addTestSuite(TestRVA.class);
result.addTestSuite(TestSheet.class); result.addTestSuite(TestSheet.class);
result.addTestSuite(TestSheetAdditional.class); result.addTestSuite(TestSheetAdditional.class);

View File

@ -0,0 +1,60 @@
/* ====================================================================
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.
==================================================================== */
package org.apache.poi.hssf.model;
import java.util.Arrays;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
/**
* Tests for {@link RowBlocksReader}
*
* @author Josh Micich
*/
public final class TestRowBlocksReader extends TestCase {
public void testAbnormalPivotTableRecords_bug46280() {
int SXVIEW_SID = 0x00B0;
Record[] inRecs = {
new RowRecord(0),
new NumberRecord(),
// normally MSODRAWING(0x00EC) would come here before SXVIEW
new UnknownRecord(SXVIEW_SID, "dummydata (SXVIEW: View Definition)".getBytes()),
new WindowTwoRecord(),
};
RecordStream rs = new RecordStream(Arrays.asList(inRecs), 0);
RowBlocksReader rbr = new RowBlocksReader(rs);
if (rs.peekNextClass() == WindowTwoRecord.class) {
// Should have stopped at the SXVIEW record
throw new AssertionFailedError("Identified bug 46280b");
}
RecordStream rbStream = rbr.getPlainRecordStream();
assertEquals(inRecs[0], rbStream.getNext());
assertEquals(inRecs[1], rbStream.getNext());
assertFalse(rbStream.hasNext());
assertTrue(rs.hasNext());
assertEquals(inRecs[2], rs.getNext());
assertEquals(inRecs[3], rs.getNext());
}
}

View File

@ -21,24 +21,30 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.record.ArrayRecord; import org.apache.poi.hssf.record.ArrayRecord;
import org.apache.poi.hssf.record.ContinueRecord;
import org.apache.poi.hssf.record.FormulaRecord; import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.hssf.record.SharedFormulaRecord;
import org.apache.poi.hssf.record.SharedValueRecordBase; import org.apache.poi.hssf.record.SharedValueRecordBase;
import org.apache.poi.hssf.record.TableRecord; import org.apache.poi.hssf.record.TableRecord;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.RecordInspector; import org.apache.poi.hssf.usermodel.RecordInspector;
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
import org.apache.poi.hssf.util.CellRangeAddress8Bit; import org.apache.poi.hssf.util.CellRangeAddress8Bit;
/** /**
* * Tests for {@link RowRecordsAggregate}
*/ */
public final class TestRowRecordsAggregate extends TestCase { public final class TestRowRecordsAggregate extends TestCase {
@ -113,4 +119,37 @@ public final class TestRowRecordsAggregate extends TestCase {
assertEquals(range.getFirstRow(), firstFormula.getRow()); assertEquals(range.getFirstRow(), firstFormula.getRow());
assertEquals(range.getFirstColumn(), firstFormula.getColumn()); assertEquals(range.getFirstColumn(), firstFormula.getColumn());
} }
/**
* This problem was noted as the overt symptom of bug 46280. The logic for skipping {@link
* UnknownRecord}s in the constructor {@link RowRecordsAggregate} did not allow for the
* possibility of tailing {@link ContinueRecord}s.<br/>
* The functionality change being tested here is actually not critical to the overall fix
* for bug 46280, since the fix involved making sure the that offending <i>PivotTable</i>
* records do not get into {@link RowRecordsAggregate}.<br/>
* This fix in {@link RowRecordsAggregate} was implemented anyway since any {@link
* UnknownRecord} has the potential of being 'continued'.
*/
public void testUnknownContinue_bug46280() {
Record[] inRecs = {
new RowRecord(0),
new NumberRecord(),
new UnknownRecord(0x5555, "dummydata".getBytes()),
new ContinueRecord("moredummydata".getBytes()),
};
RecordStream rs = new RecordStream(Arrays.asList(inRecs), 0);
RowRecordsAggregate rra;
try {
rra = new RowRecordsAggregate(rs, SharedValueManager.EMPTY);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Unexpected record type")) {
throw new AssertionFailedError("Identified bug 46280a");
}
throw e;
}
RecordCollector rv = new RecordCollector();
rra.visitContainedRecords(rv);
Record[] outRecs = rv.getRecords();
assertEquals(5, outRecs.length);
}
} }