diff options
Diffstat (limited to 'src')
4 files changed, 542 insertions, 495 deletions
diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 991bd0ec16..80a6e8cee5 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ <!-- Don't forget to update status.xml too! --> <release version="3.5-beta5" date="2008-??-??"> + <action dev="POI-DEVELOPERS" type="fix">46312 - Fixed ValueRecordsAggregate to handle removal of new empty row</action> <action dev="POI-DEVELOPERS" type="add">46269 - Improved error message when attempting to read BIFF2 file</action> <action dev="POI-DEVELOPERS" type="fix">46206 - Fixed Sheet to tolerate missing DIMENSION records</action> <action dev="POI-DEVELOPERS" type="add">46301 - added pivot table records: SXDI, SXVDEX, SXPI, SXIDSTM, SXVIEW, SXVD, SXVS, et al</action> diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 1a1fd30ebe..8763adc0a0 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ <!-- Don't forget to update changes.xml too! --> <changes> <release version="3.5-beta5" date="2008-??-??"> + <action dev="POI-DEVELOPERS" type="fix">46312 - Fixed ValueRecordsAggregate to handle removal of new empty row</action> <action dev="POI-DEVELOPERS" type="add">46269 - Improved error message when attempting to read BIFF2 file</action> <action dev="POI-DEVELOPERS" type="fix">46206 - Fixed Sheet to tolerate missing DIMENSION records</action> <action dev="POI-DEVELOPERS" type="add">46301 - added pivot table records: SXDI, SXVDEX, SXPI, SXIDSTM, SXVIEW, SXVD, SXVS, et al</action> diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java index 27000811ce..6ee871a7f2 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java @@ -40,279 +40,291 @@ import org.apache.poi.hssf.record.formula.Ptg; * @author Jason Height (jheight at chariot dot net dot au) */ public final class ValueRecordsAggregate { - private int firstcell = -1; - private int lastcell = -1; - private CellValueRecordInterface[][] records; - - /** Creates a new instance of ValueRecordsAggregate */ - - public ValueRecordsAggregate() - { - records = new CellValueRecordInterface[30][]; // We start with 30 Rows. - } - - public void insertCell(CellValueRecordInterface cell) { - short column = cell.getColumn(); - int row = cell.getRow(); - if (row >= records.length) { - CellValueRecordInterface[][] oldRecords = records; - int newSize = oldRecords.length * 2; - if(newSize<row+1) newSize=row+1; - records = new CellValueRecordInterface[newSize][]; - System.arraycopy(oldRecords, 0, records, 0, oldRecords.length); - } - CellValueRecordInterface[] rowCells = records[row]; - if (rowCells == null) { - int newSize = column + 1; - if(newSize<10) newSize=10; - rowCells = new CellValueRecordInterface[newSize]; - records[row] = rowCells; - } - if (column >= rowCells.length) { - CellValueRecordInterface[] oldRowCells = rowCells; - int newSize = oldRowCells.length * 2; - if(newSize<column+1) newSize=column+1; - // if(newSize>257) newSize=257; // activate? - rowCells = new CellValueRecordInterface[newSize]; - System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length); - records[row] = rowCells; - } - rowCells[column] = cell; - - if ((column < firstcell) || (firstcell == -1)) { - firstcell = column; - } - if ((column > lastcell) || (lastcell == -1)) { - lastcell = column; - } - } - - public void removeCell(CellValueRecordInterface cell) { - if (cell == null) { - throw new IllegalArgumentException("cell must not be null"); - } - int row = cell.getRow(); - if (row >= records.length) { - throw new RuntimeException("cell row is out of range"); - } - CellValueRecordInterface[] rowCells = records[row]; - if (rowCells == null) { - throw new RuntimeException("cell row is already empty"); - } - short column = cell.getColumn(); - if (column >= rowCells.length) { - throw new RuntimeException("cell column is out of range"); - } - rowCells[column] = null; - } - - public void removeAllCellsValuesForRow(int rowIndex) { - if (rowIndex >= records.length) { - throw new IllegalArgumentException("Specified rowIndex " + rowIndex - + " is outside the allowable range (0.." +records.length + ")"); - } - records[rowIndex] = null; - } - - - public int getPhysicalNumberOfCells() - { - int count=0; - for(int r=0;r<records.length;r++) { - CellValueRecordInterface[] rowCells=records[r]; - if (rowCells != null) - for(short c=0;c<rowCells.length;c++) { - if(rowCells[c]!=null) count++; - } - } - return count; - } - - public int getFirstCellNum() - { - return firstcell; - } - - public int getLastCellNum() - { - return lastcell; - } - - /** - * Processes a single cell value record - * @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet - */ - public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) { - if (rec instanceof FormulaRecord) { - FormulaRecord formulaRec = (FormulaRecord)rec; - // read optional cached text value - StringRecord cachedText; - Class nextClass = rs.peekNextClass(); - if (nextClass == StringRecord.class) { - cachedText = (StringRecord) rs.getNext(); - } else { - cachedText = null; - } - insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh)); - } else { - insertCell(rec); - } - } - - /** Tallies a count of the size of the cell records - * that are attached to the rows in the range specified. - */ - public int getRowCellBlockSize(int startRow, int endRow) { - MyIterator itr = new MyIterator(startRow, endRow); - int size = 0; - while (itr.hasNext()) { - CellValueRecordInterface cell = (CellValueRecordInterface)itr.next(); - int row = cell.getRow(); - if (row > endRow) - break; - if ((row >=startRow) && (row <= endRow)) - size += ((RecordBase)cell).getRecordSize(); - } - return size; - } - - /** Returns true if the row has cells attached to it */ - public boolean rowHasCells(int row) { - if (row > records.length-1) //previously this said row > records.length which means if - return false; // if records.length == 60 and I pass "60" here I get array out of bounds - CellValueRecordInterface[] rowCells=records[row]; //because a 60 length array has the last index = 59 - if(rowCells==null) return false; - for(int col=0;col<rowCells.length;col++) { - if(rowCells[col]!=null) return true; - } - return false; - } - - /** Serializes the cells that are allocated to a certain row range*/ - public int serializeCellRow(final int row, int offset, byte [] data) - { - MyIterator itr = new MyIterator(row, row); - int pos = offset; - - while (itr.hasNext()) - { - CellValueRecordInterface cell = (CellValueRecordInterface)itr.next(); - if (cell.getRow() != row) - break; - pos += (( RecordBase ) cell).serialize(pos, data); - } - return pos - offset; - } - - public void visitCellsForRow(int rowIndex, RecordVisitor rv) { - - CellValueRecordInterface[] cellRecs = records[rowIndex]; - if (cellRecs != null) { - for (int i = 0; i < cellRecs.length; i++) { - CellValueRecordInterface cvr = cellRecs[i]; - if (cvr == null) { - continue; - } - if (cvr instanceof RecordAggregate) { - RecordAggregate agg = (RecordAggregate) cvr; - agg.visitContainedRecords(rv); - } else { - Record rec = (Record) cvr; - rv.visitRecord(rec); - } - } - } - } - - public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) { - for (int i = 0; i < records.length; i++) { - CellValueRecordInterface[] rowCells = records[i]; - if (rowCells == null) { - continue; - } - for (int j = 0; j < rowCells.length; j++) { - CellValueRecordInterface cell = rowCells[j]; - if (cell instanceof FormulaRecordAggregate) { - FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord(); - Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter? - if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) { - fr.setParsedExpression(ptgs); - } - } - } - } - } - - public CellValueRecordInterface[] getValueRecords() { - List temp = new ArrayList(); - - for (int i = 0; i < records.length; i++) { - CellValueRecordInterface[] rowCells = records[i]; - if (rowCells == null) { - continue; - } - for (int j = 0; j < rowCells.length; j++) { - CellValueRecordInterface cell = rowCells[j]; - if (cell != null) { - temp.add(cell); - } - } - } - - CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()]; - temp.toArray(result); - return result; - } - public Iterator getIterator() - { - return new MyIterator(); - } - - private final class MyIterator implements Iterator { - short nextColumn=-1; - int nextRow,lastRow; - - public MyIterator() - { - this.nextRow=0; - this.lastRow=records.length-1; - findNext(); - } - - public MyIterator(int firstRow,int lastRow) - { - this.nextRow=firstRow; - this.lastRow=lastRow; - findNext(); - } - - public boolean hasNext() { - return nextRow<=lastRow; - } - public Object next() { - Object o=records[nextRow][nextColumn]; - findNext(); - return o; - } - public void remove() { - throw new UnsupportedOperationException("gibt's noch nicht"); - } - - private void findNext() { - nextColumn++; - for(;nextRow<=lastRow;nextRow++) { - //previously this threw array out of bounds... - CellValueRecordInterface[] rowCells=(nextRow < records.length) ? records[nextRow] : null; - if(rowCells==null) { // This row is empty - nextColumn=0; - continue; - } - for(;nextColumn<rowCells.length;nextColumn++) { - if(rowCells[nextColumn]!=null) return; - } - nextColumn=0; - } - } - - } + private static final int MAX_ROW_INDEX = 0XFFFF; + private int firstcell = -1; + private int lastcell = -1; + private CellValueRecordInterface[][] records; + + /** Creates a new instance of ValueRecordsAggregate */ + + public ValueRecordsAggregate() { + records = new CellValueRecordInterface[30][]; // We start with 30 Rows. + } + + public void insertCell(CellValueRecordInterface cell) { + short column = cell.getColumn(); + int row = cell.getRow(); + if (row >= records.length) { + CellValueRecordInterface[][] oldRecords = records; + int newSize = oldRecords.length * 2; + if (newSize < row + 1) + newSize = row + 1; + records = new CellValueRecordInterface[newSize][]; + System.arraycopy(oldRecords, 0, records, 0, oldRecords.length); + } + CellValueRecordInterface[] rowCells = records[row]; + if (rowCells == null) { + int newSize = column + 1; + if (newSize < 10) + newSize = 10; + rowCells = new CellValueRecordInterface[newSize]; + records[row] = rowCells; + } + if (column >= rowCells.length) { + CellValueRecordInterface[] oldRowCells = rowCells; + int newSize = oldRowCells.length * 2; + if (newSize < column + 1) + newSize = column + 1; + // if(newSize>257) newSize=257; // activate? + rowCells = new CellValueRecordInterface[newSize]; + System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length); + records[row] = rowCells; + } + rowCells[column] = cell; + + if ((column < firstcell) || (firstcell == -1)) { + firstcell = column; + } + if ((column > lastcell) || (lastcell == -1)) { + lastcell = column; + } + } + + public void removeCell(CellValueRecordInterface cell) { + if (cell == null) { + throw new IllegalArgumentException("cell must not be null"); + } + int row = cell.getRow(); + if (row >= records.length) { + throw new RuntimeException("cell row is out of range"); + } + CellValueRecordInterface[] rowCells = records[row]; + if (rowCells == null) { + throw new RuntimeException("cell row is already empty"); + } + short column = cell.getColumn(); + if (column >= rowCells.length) { + throw new RuntimeException("cell column is out of range"); + } + rowCells[column] = null; + } + + public void removeAllCellsValuesForRow(int rowIndex) { + if (rowIndex < 0 || rowIndex > MAX_ROW_INDEX) { + throw new IllegalArgumentException("Specified rowIndex " + rowIndex + + " is outside the allowable range (0.." +MAX_ROW_INDEX + ")"); + } + if (rowIndex >= records.length) { + // this can happen when the client code has created a row, + // and then removes/replaces it before adding any cells. (see bug 46312) + return; + } + + records[rowIndex] = null; + } + + + public int getPhysicalNumberOfCells() { + int count = 0; + for (int r = 0; r < records.length; r++) { + CellValueRecordInterface[] rowCells = records[r]; + if (rowCells != null) { + for (int c = 0; c < rowCells.length; c++) { + if (rowCells[c] != null) + count++; + } + } + } + return count; + } + + public int getFirstCellNum() { + return firstcell; + } + + public int getLastCellNum() { + return lastcell; + } + + /** + * Processes a single cell value record + * @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet + */ + public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) { + if (rec instanceof FormulaRecord) { + FormulaRecord formulaRec = (FormulaRecord)rec; + // read optional cached text value + StringRecord cachedText; + Class nextClass = rs.peekNextClass(); + if (nextClass == StringRecord.class) { + cachedText = (StringRecord) rs.getNext(); + } else { + cachedText = null; + } + insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh)); + } else { + insertCell(rec); + } + } + + /** Tallies a count of the size of the cell records + * that are attached to the rows in the range specified. + */ + public int getRowCellBlockSize(int startRow, int endRow) { + MyIterator itr = new MyIterator(records, startRow, endRow); + int size = 0; + while (itr.hasNext()) { + CellValueRecordInterface cell = (CellValueRecordInterface) itr.next(); + int row = cell.getRow(); + if (row > endRow) { + break; + } + if ((row >= startRow) && (row <= endRow)) { + size += ((RecordBase) cell).getRecordSize(); + } + } + return size; + } + + /** Returns true if the row has cells attached to it */ + public boolean rowHasCells(int row) { + if (row >= records.length) { + return false; + } + CellValueRecordInterface[] rowCells=records[row]; + if(rowCells==null) return false; + for(int col=0;col<rowCells.length;col++) { + if(rowCells[col]!=null) return true; + } + return false; + } + + /** Serializes the cells that are allocated to a certain row range*/ + public int serializeCellRow(final int row, int offset, byte [] data) + { + MyIterator itr = new MyIterator(records, row, row); + int pos = offset; + + while (itr.hasNext()) + { + CellValueRecordInterface cell = (CellValueRecordInterface)itr.next(); + if (cell.getRow() != row) + break; + pos += (( RecordBase ) cell).serialize(pos, data); + } + return pos - offset; + } + + public void visitCellsForRow(int rowIndex, RecordVisitor rv) { + + CellValueRecordInterface[] cellRecs = records[rowIndex]; + if (cellRecs != null) { + for (int i = 0; i < cellRecs.length; i++) { + CellValueRecordInterface cvr = cellRecs[i]; + if (cvr == null) { + continue; + } + if (cvr instanceof RecordAggregate) { + RecordAggregate agg = (RecordAggregate) cvr; + agg.visitContainedRecords(rv); + } else { + Record rec = (Record) cvr; + rv.visitRecord(rec); + } + } + } + } + + public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) { + for (int i = 0; i < records.length; i++) { + CellValueRecordInterface[] rowCells = records[i]; + if (rowCells == null) { + continue; + } + for (int j = 0; j < rowCells.length; j++) { + CellValueRecordInterface cell = rowCells[j]; + if (cell instanceof FormulaRecordAggregate) { + FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord(); + Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter? + if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) { + fr.setParsedExpression(ptgs); + } + } + } + } + } + + public CellValueRecordInterface[] getValueRecords() { + List<CellValueRecordInterface> temp = new ArrayList<CellValueRecordInterface>(); + + for (int i = 0; i < records.length; i++) { + CellValueRecordInterface[] rowCells = records[i]; + if (rowCells == null) { + continue; + } + for (int j = 0; j < rowCells.length; j++) { + CellValueRecordInterface cell = rowCells[j]; + if (cell != null) { + temp.add(cell); + } + } + } + + CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()]; + temp.toArray(result); + return result; + } + public Iterator getIterator() { + return new MyIterator(records); + } + + private static final class MyIterator implements Iterator { + private final CellValueRecordInterface[][] records; + private short nextColumn = -1; + private int nextRow, lastRow; + + public MyIterator(CellValueRecordInterface[][] pRecords) { + this(pRecords, 0, pRecords.length - 1); + } + + public MyIterator(CellValueRecordInterface[][] pRecords, int firstRow, int lastRow) { + records = pRecords; + this.nextRow = firstRow; + this.lastRow = lastRow; + findNext(); + } + + public boolean hasNext() { + return nextRow <= lastRow; + } + + public Object next() { + Object o = records[nextRow][nextColumn]; + findNext(); + return o; + } + + public void remove() { + throw new UnsupportedOperationException("gibt's noch nicht"); + } + + private void findNext() { + nextColumn++; + for (; nextRow <= lastRow; nextRow++) { + // previously this threw array out of bounds... + CellValueRecordInterface[] rowCells = (nextRow < records.length) ? records[nextRow] + : null; + if (rowCells == null) { // This row is empty + nextColumn = 0; + continue; + } + for (; nextColumn < rowCells.length; nextColumn++) { + if (rowCells[nextColumn] != null) + return; + } + nextColumn = 0; + } + } + + } } diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java index c62f725238..af119f29b9 100755 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java @@ -37,249 +37,282 @@ import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RecordBase; import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +/** + * Tests for {@link ValueRecordsAggregate} + */ public final class TestValueRecordsAggregate extends TestCase { - private static final String ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE = "AbnormalSharedFormulaFlag.xls"; - private final ValueRecordsAggregate valueRecord = new ValueRecordsAggregate(); + private static final String ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE = "AbnormalSharedFormulaFlag.xls"; + private final ValueRecordsAggregate valueRecord = new ValueRecordsAggregate(); - /** - * Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed - * as part of the value records - */ - public void testSharedFormula() { - List records = new ArrayList(); - records.add( new FormulaRecord() ); - records.add( new SharedFormulaRecord() ); - records.add(new WindowTwoRecord()); + /** + * Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed + * as part of the value records + */ + public void testSharedFormula() { + List<Record> records = new ArrayList<Record>(); + records.add(new FormulaRecord()); + records.add(new SharedFormulaRecord()); + records.add(new WindowTwoRecord()); - constructValueRecord(records); - Iterator iterator = valueRecord.getIterator(); - RecordBase record = (RecordBase) iterator.next(); - assertNotNull( "Row contains a value", record ); - assertTrue( "First record is a FormulaRecordsAggregate", ( record instanceof FormulaRecordAggregate ) ); - //Ensure that the SharedFormulaRecord has been converted - assertFalse( "SharedFormulaRecord is null", iterator.hasNext() ); + constructValueRecord(records); + Iterator iterator = valueRecord.getIterator(); + RecordBase record = (RecordBase) iterator.next(); + assertNotNull( "Row contains a value", record ); + assertTrue( "First record is a FormulaRecordsAggregate", ( record instanceof FormulaRecordAggregate ) ); + //Ensure that the SharedFormulaRecord has been converted + assertFalse( "SharedFormulaRecord is null", iterator.hasNext() ); - } + } - private void constructValueRecord(List records) { - RowBlocksReader rbr = new RowBlocksReader(new RecordStream(records, 0)); - SharedValueManager sfrh = rbr.getSharedFormulaManager(); - RecordStream rs = rbr.getPlainRecordStream(); - while(rs.hasNext()) { - Record rec = rs.getNext(); - valueRecord.construct((CellValueRecordInterface)rec, rs, sfrh); - } - } + private void constructValueRecord(List records) { + RowBlocksReader rbr = new RowBlocksReader(new RecordStream(records, 0)); + SharedValueManager sfrh = rbr.getSharedFormulaManager(); + RecordStream rs = rbr.getPlainRecordStream(); + while(rs.hasNext()) { + Record rec = rs.getNext(); + valueRecord.construct((CellValueRecordInterface)rec, rs, sfrh); + } + } - private static List testData() { - List records = new ArrayList(); - FormulaRecord formulaRecord = new FormulaRecord(); - BlankRecord blankRecord = new BlankRecord(); - formulaRecord.setRow( 1 ); - formulaRecord.setColumn( (short) 1 ); - blankRecord.setRow( 2 ); - blankRecord.setColumn( (short) 2 ); - records.add( formulaRecord ); - records.add( blankRecord ); - records.add(new WindowTwoRecord()); - return records; - } + private static List testData() { + List<Record> records = new ArrayList<Record>(); + FormulaRecord formulaRecord = new FormulaRecord(); + BlankRecord blankRecord = new BlankRecord(); + formulaRecord.setRow(1); + formulaRecord.setColumn((short) 1); + blankRecord.setRow(2); + blankRecord.setColumn((short) 2); + records.add(formulaRecord); + records.add(blankRecord); + records.add(new WindowTwoRecord()); + return records; + } - public void testInsertCell() { - Iterator iterator = valueRecord.getIterator(); - assertFalse( iterator.hasNext() ); + public void testInsertCell() { + Iterator iterator = valueRecord.getIterator(); + assertFalse( iterator.hasNext() ); - BlankRecord blankRecord = newBlankRecord(); - valueRecord.insertCell( blankRecord ); - iterator = valueRecord.getIterator(); - assertTrue( iterator.hasNext() ); - } + BlankRecord blankRecord = newBlankRecord(); + valueRecord.insertCell( blankRecord ); + iterator = valueRecord.getIterator(); + assertTrue( iterator.hasNext() ); + } - public void testRemoveCell() { - BlankRecord blankRecord1 = newBlankRecord(); - valueRecord.insertCell( blankRecord1 ); - BlankRecord blankRecord2 = newBlankRecord(); - valueRecord.removeCell( blankRecord2 ); - Iterator iterator = valueRecord.getIterator(); - assertFalse( iterator.hasNext() ); + public void testRemoveCell() { + BlankRecord blankRecord1 = newBlankRecord(); + valueRecord.insertCell( blankRecord1 ); + BlankRecord blankRecord2 = newBlankRecord(); + valueRecord.removeCell( blankRecord2 ); + Iterator iterator = valueRecord.getIterator(); + assertFalse( iterator.hasNext() ); - // removing an already empty cell just falls through - valueRecord.removeCell( blankRecord2 ); - } + // removing an already empty cell just falls through + valueRecord.removeCell( blankRecord2 ); + } - public void testGetPhysicalNumberOfCells() { - assertEquals(0, valueRecord.getPhysicalNumberOfCells()); - BlankRecord blankRecord1 = newBlankRecord(); - valueRecord.insertCell( blankRecord1 ); - assertEquals(1, valueRecord.getPhysicalNumberOfCells()); - valueRecord.removeCell( blankRecord1 ); - assertEquals(0, valueRecord.getPhysicalNumberOfCells()); - } + public void testGetPhysicalNumberOfCells() { + assertEquals(0, valueRecord.getPhysicalNumberOfCells()); + BlankRecord blankRecord1 = newBlankRecord(); + valueRecord.insertCell( blankRecord1 ); + assertEquals(1, valueRecord.getPhysicalNumberOfCells()); + valueRecord.removeCell( blankRecord1 ); + assertEquals(0, valueRecord.getPhysicalNumberOfCells()); + } - public void testGetFirstCellNum() { - assertEquals( -1, valueRecord.getFirstCellNum() ); - valueRecord.insertCell( newBlankRecord( 2, 2 ) ); - assertEquals( 2, valueRecord.getFirstCellNum() ); - valueRecord.insertCell( newBlankRecord( 3, 3 ) ); - assertEquals( 2, valueRecord.getFirstCellNum() ); + public void testGetFirstCellNum() { + assertEquals( -1, valueRecord.getFirstCellNum() ); + valueRecord.insertCell( newBlankRecord( 2, 2 ) ); + assertEquals( 2, valueRecord.getFirstCellNum() ); + valueRecord.insertCell( newBlankRecord( 3, 3 ) ); + assertEquals( 2, valueRecord.getFirstCellNum() ); - // Note: Removal doesn't currently reset the first column. It probably should but it doesn't. - valueRecord.removeCell( newBlankRecord( 2, 2 ) ); - assertEquals( 2, valueRecord.getFirstCellNum() ); - } + // Note: Removal doesn't currently reset the first column. It probably should but it doesn't. + valueRecord.removeCell( newBlankRecord( 2, 2 ) ); + assertEquals( 2, valueRecord.getFirstCellNum() ); + } - public void testGetLastCellNum() { - assertEquals( -1, valueRecord.getLastCellNum() ); - valueRecord.insertCell( newBlankRecord( 2, 2 ) ); - assertEquals( 2, valueRecord.getLastCellNum() ); - valueRecord.insertCell( newBlankRecord( 3, 3 ) ); - assertEquals( 3, valueRecord.getLastCellNum() ); + public void testGetLastCellNum() { + assertEquals( -1, valueRecord.getLastCellNum() ); + valueRecord.insertCell( newBlankRecord( 2, 2 ) ); + assertEquals( 2, valueRecord.getLastCellNum() ); + valueRecord.insertCell( newBlankRecord( 3, 3 ) ); + assertEquals( 3, valueRecord.getLastCellNum() ); - // Note: Removal doesn't currently reset the last column. It probably should but it doesn't. - valueRecord.removeCell( newBlankRecord( 3, 3 ) ); - assertEquals( 3, valueRecord.getLastCellNum() ); + // Note: Removal doesn't currently reset the last column. It probably should but it doesn't. + valueRecord.removeCell( newBlankRecord( 3, 3 ) ); + assertEquals( 3, valueRecord.getLastCellNum() ); - } + } - public void testSerialize() { - byte[] actualArray = new byte[36]; - byte[] expectedArray = new byte[] - { - (byte)0x06, (byte)0x00, (byte)0x16, (byte)0x00, - (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x02, - (byte)0x06, (byte)0x00, (byte)0x02, (byte)0x00, - (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, - }; - List records = testData(); - constructValueRecord(records); - int bytesWritten = valueRecord.serializeCellRow(1, 0, actualArray ); - bytesWritten += valueRecord.serializeCellRow(2, bytesWritten, actualArray ); - assertEquals( 36, bytesWritten ); - for (int i = 0; i < 36; i++) - assertEquals( expectedArray[i], actualArray[i] ); - } + public void testSerialize() { + byte[] actualArray = new byte[36]; + byte[] expectedArray = new byte[] + { + (byte)0x06, (byte)0x00, (byte)0x16, (byte)0x00, + (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x02, + (byte)0x06, (byte)0x00, (byte)0x02, (byte)0x00, + (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, + }; + List records = testData(); + constructValueRecord(records); + int bytesWritten = valueRecord.serializeCellRow(1, 0, actualArray ); + bytesWritten += valueRecord.serializeCellRow(2, bytesWritten, actualArray ); + assertEquals( 36, bytesWritten ); + for (int i = 0; i < 36; i++) + assertEquals( expectedArray[i], actualArray[i] ); + } - private static BlankRecord newBlankRecord() - { - return newBlankRecord( 2, 2 ); - } + private static BlankRecord newBlankRecord() + { + return newBlankRecord( 2, 2 ); + } - private static BlankRecord newBlankRecord( int col, int row) - { - BlankRecord blankRecord = new BlankRecord(); - blankRecord.setRow( row ); - blankRecord.setColumn( (short) col ); - return blankRecord; - } + private static BlankRecord newBlankRecord( int col, int row) + { + BlankRecord blankRecord = new BlankRecord(); + blankRecord.setRow( row ); + blankRecord.setColumn( (short) col ); + return blankRecord; + } - /** - * Sometimes the 'shared formula' flag (<tt>FormulaRecord.isSharedFormula()</tt>) is set when - * there is no corresponding SharedFormulaRecord available. SharedFormulaRecord definitions do - * not span multiple sheets. They are are only defined within a sheet, and thus they do not - * have a sheet index field (only row and column range fields).<br/> - * So it is important that the code which locates the SharedFormulaRecord for each - * FormulaRecord does not allow matches across sheets.</br> - * - * Prior to bugzilla 44449 (Feb 2008), POI <tt>ValueRecordsAggregate.construct(int, List)</tt> - * allowed <tt>SharedFormulaRecord</tt>s to be erroneously used across sheets. That incorrect - * behaviour is shown by this test.<p/> - * - * <b>Notes on how to produce the test spreadsheet</b>:</p> - * The setup for this test (AbnormalSharedFormulaFlag.xls) is rather fragile, insomuchas - * re-saving the file (either with Excel or POI) clears the flag.<br/> - * <ol> - * <li>A new spreadsheet was created in Excel (File | New | Blank Workbook).</li> - * <li>Sheet3 was deleted.</li> - * <li>Sheet2!A1 formula was set to '="second formula"', and fill-dragged through A1:A8.</li> - * <li>Sheet1!A1 formula was set to '="first formula"', and also fill-dragged through A1:A8.</li> - * <li>Four rows on Sheet1 "5" through "8" were deleted ('delete rows' alt-E D, not 'clear' Del).</li> - * <li>The spreadsheet was saved as AbnormalSharedFormulaFlag.xls.</li> - * </ol> - * Prior to the row delete action the spreadsheet has two <tt>SharedFormulaRecord</tt>s. One - * for each sheet. To expose the bug, the shared formulas have been made to overlap.<br/> - * The row delete action (as described here) seems to to delete the - * <tt>SharedFormulaRecord</tt> from Sheet1 (but not clear the 'shared formula' flags.<br/> - * There are other variations on this theme to create the same effect. - * - */ - public void testSpuriousSharedFormulaFlag() { - - long actualCRC = getFileCRC(HSSFTestDataSamples.openSampleFileStream(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE)); - long expectedCRC = 2277445406L; - if(actualCRC != expectedCRC) { - System.err.println("Expected crc " + expectedCRC + " but got " + actualCRC); - throw failUnexpectedTestFileChange(); - } - HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE); - - HSSFSheet s = wb.getSheetAt(0); // Sheet1 - - String cellFormula; - cellFormula = getFormulaFromFirstCell(s, 0); // row "1" - // the problem is not observable in the first row of the shared formula - if(!cellFormula.equals("\"first formula\"")) { - throw new RuntimeException("Something else wrong with this test case"); - } - - // but the problem is observable in rows 2,3,4 - cellFormula = getFormulaFromFirstCell(s, 1); // row "2" - if(cellFormula.equals("\"second formula\"")) { - throw new AssertionFailedError("found bug 44449 (Wrong SharedFormulaRecord was used)."); - } - if(!cellFormula.equals("\"first formula\"")) { - throw new RuntimeException("Something else wrong with this test case"); - } - } - private static String getFormulaFromFirstCell(HSSFSheet s, int rowIx) { - return s.getRow(rowIx).getCell(0).getCellFormula(); - } + /** + * Sometimes the 'shared formula' flag (<tt>FormulaRecord.isSharedFormula()</tt>) is set when + * there is no corresponding SharedFormulaRecord available. SharedFormulaRecord definitions do + * not span multiple sheets. They are are only defined within a sheet, and thus they do not + * have a sheet index field (only row and column range fields).<br/> + * So it is important that the code which locates the SharedFormulaRecord for each + * FormulaRecord does not allow matches across sheets.</br> + * + * Prior to bugzilla 44449 (Feb 2008), POI <tt>ValueRecordsAggregate.construct(int, List)</tt> + * allowed <tt>SharedFormulaRecord</tt>s to be erroneously used across sheets. That incorrect + * behaviour is shown by this test.<p/> + * + * <b>Notes on how to produce the test spreadsheet</b>:</p> + * The setup for this test (AbnormalSharedFormulaFlag.xls) is rather fragile, insomuchas + * re-saving the file (either with Excel or POI) clears the flag.<br/> + * <ol> + * <li>A new spreadsheet was created in Excel (File | New | Blank Workbook).</li> + * <li>Sheet3 was deleted.</li> + * <li>Sheet2!A1 formula was set to '="second formula"', and fill-dragged through A1:A8.</li> + * <li>Sheet1!A1 formula was set to '="first formula"', and also fill-dragged through A1:A8.</li> + * <li>Four rows on Sheet1 "5" through "8" were deleted ('delete rows' alt-E D, not 'clear' Del).</li> + * <li>The spreadsheet was saved as AbnormalSharedFormulaFlag.xls.</li> + * </ol> + * Prior to the row delete action the spreadsheet has two <tt>SharedFormulaRecord</tt>s. One + * for each sheet. To expose the bug, the shared formulas have been made to overlap.<br/> + * The row delete action (as described here) seems to to delete the + * <tt>SharedFormulaRecord</tt> from Sheet1 (but not clear the 'shared formula' flags.<br/> + * There are other variations on this theme to create the same effect. + * + */ + public void testSpuriousSharedFormulaFlag() { + + long actualCRC = getFileCRC(HSSFTestDataSamples.openSampleFileStream(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE)); + long expectedCRC = 2277445406L; + if(actualCRC != expectedCRC) { + System.err.println("Expected crc " + expectedCRC + " but got " + actualCRC); + throw failUnexpectedTestFileChange(); + } + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE); + + HSSFSheet s = wb.getSheetAt(0); // Sheet1 + + String cellFormula; + cellFormula = getFormulaFromFirstCell(s, 0); // row "1" + // the problem is not observable in the first row of the shared formula + if(!cellFormula.equals("\"first formula\"")) { + throw new RuntimeException("Something else wrong with this test case"); + } + + // but the problem is observable in rows 2,3,4 + cellFormula = getFormulaFromFirstCell(s, 1); // row "2" + if(cellFormula.equals("\"second formula\"")) { + throw new AssertionFailedError("found bug 44449 (Wrong SharedFormulaRecord was used)."); + } + if(!cellFormula.equals("\"first formula\"")) { + throw new RuntimeException("Something else wrong with this test case"); + } + } + private static String getFormulaFromFirstCell(HSSFSheet s, int rowIx) { + return s.getRow(rowIx).getCell(0).getCellFormula(); + } - /** - * If someone opened this particular test file in Excel and saved it, the peculiar condition - * which causes the target bug would probably disappear. This test would then just succeed - * regardless of whether the fix was present. So a CRC check is performed to make it less easy - * for that to occur. - */ - private static RuntimeException failUnexpectedTestFileChange() { - String msg = "Test file '" + ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE + "' has changed. " - + "This junit may not be properly testing for the target bug. " - + "Either revert the test file or ensure that the new version " - + "has the right characteristics to test the target bug."; - // A breakpoint in ValueRecordsAggregate.handleMissingSharedFormulaRecord(FormulaRecord) - // should get hit during parsing of Sheet1. - // If the test spreadsheet is created as directed, this condition should occur. - // It is easy to upset the test spreadsheet (for example re-saving will destroy the - // peculiar condition we are testing for). - throw new RuntimeException(msg); - } + /** + * If someone opened this particular test file in Excel and saved it, the peculiar condition + * which causes the target bug would probably disappear. This test would then just succeed + * regardless of whether the fix was present. So a CRC check is performed to make it less easy + * for that to occur. + */ + private static RuntimeException failUnexpectedTestFileChange() { + String msg = "Test file '" + ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE + "' has changed. " + + "This junit may not be properly testing for the target bug. " + + "Either revert the test file or ensure that the new version " + + "has the right characteristics to test the target bug."; + // A breakpoint in ValueRecordsAggregate.handleMissingSharedFormulaRecord(FormulaRecord) + // should get hit during parsing of Sheet1. + // If the test spreadsheet is created as directed, this condition should occur. + // It is easy to upset the test spreadsheet (for example re-saving will destroy the + // peculiar condition we are testing for). + throw new RuntimeException(msg); + } - /** - * gets a CRC checksum for the content of a file - */ - private static long getFileCRC(InputStream is) { - CRC32 crc = new CRC32(); - byte[] buf = new byte[2048]; - try { - while(true) { - int bytesRead = is.read(buf); - if(bytesRead < 1) { - break; - } - crc.update(buf, 0, bytesRead); - } - is.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - - return crc.getValue(); - } + /** + * gets a CRC checksum for the content of a file + */ + private static long getFileCRC(InputStream is) { + CRC32 crc = new CRC32(); + byte[] buf = new byte[2048]; + try { + while(true) { + int bytesRead = is.read(buf); + if(bytesRead < 1) { + break; + } + crc.update(buf, 0, bytesRead); + } + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return crc.getValue(); + } + public void testRemoveNewRow_bug46312() { + // To make bug occur, rowIndex needs to be >= ValueRecordsAggregate.records.length + int rowIndex = 30; + + ValueRecordsAggregate vra = new ValueRecordsAggregate(); + try { + vra.removeAllCellsValuesForRow(rowIndex); + } catch (IllegalArgumentException e) { + if (e.getMessage().equals("Specified rowIndex 30 is outside the allowable range (0..30)")) { + throw new AssertionFailedError("Identified bug 46312"); + } + throw e; + } + + if (false) { // same bug as demonstrated through usermodel API + + HSSFWorkbook wb = new HSSFWorkbook(); + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(rowIndex); + if (false) { // must not add any cells to the new row if we want to see the bug + row.createCell(0); // this causes ValueRecordsAggregate.records to auto-extend + } + try { + sheet.createRow(rowIndex); + } catch (IllegalArgumentException e) { + throw new AssertionFailedError("Identified bug 46312"); + } + } + } } |