Fix for bug 46312 - ValueRecordsAggregate should handle removal of new empty row
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@722206 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
886de704ff
commit
2014a596be
@ -37,6 +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">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="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="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>
|
<action dev="POI-DEVELOPERS" type="add">46301 - added pivot table records: SXDI, SXVDEX, SXPI, SXIDSTM, SXVIEW, SXVD, SXVS, et al</action>
|
||||||
|
@ -34,6 +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">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="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="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>
|
<action dev="POI-DEVELOPERS" type="add">46301 - added pivot table records: SXDI, SXVDEX, SXPI, SXIDSTM, SXVIEW, SXVD, SXVS, et al</action>
|
||||||
|
@ -40,279 +40,291 @@ import org.apache.poi.hssf.record.formula.Ptg;
|
|||||||
* @author Jason Height (jheight at chariot dot net dot au)
|
* @author Jason Height (jheight at chariot dot net dot au)
|
||||||
*/
|
*/
|
||||||
public final class ValueRecordsAggregate {
|
public final class ValueRecordsAggregate {
|
||||||
private int firstcell = -1;
|
private static final int MAX_ROW_INDEX = 0XFFFF;
|
||||||
private int lastcell = -1;
|
private int firstcell = -1;
|
||||||
private CellValueRecordInterface[][] records;
|
private int lastcell = -1;
|
||||||
|
private CellValueRecordInterface[][] records;
|
||||||
|
|
||||||
/** Creates a new instance of ValueRecordsAggregate */
|
/** Creates a new instance of ValueRecordsAggregate */
|
||||||
|
|
||||||
public ValueRecordsAggregate()
|
public ValueRecordsAggregate() {
|
||||||
{
|
records = new CellValueRecordInterface[30][]; // We start with 30 Rows.
|
||||||
records = new CellValueRecordInterface[30][]; // We start with 30 Rows.
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void insertCell(CellValueRecordInterface cell) {
|
public void insertCell(CellValueRecordInterface cell) {
|
||||||
short column = cell.getColumn();
|
short column = cell.getColumn();
|
||||||
int row = cell.getRow();
|
int row = cell.getRow();
|
||||||
if (row >= records.length) {
|
if (row >= records.length) {
|
||||||
CellValueRecordInterface[][] oldRecords = records;
|
CellValueRecordInterface[][] oldRecords = records;
|
||||||
int newSize = oldRecords.length * 2;
|
int newSize = oldRecords.length * 2;
|
||||||
if(newSize<row+1) newSize=row+1;
|
if (newSize < row + 1)
|
||||||
records = new CellValueRecordInterface[newSize][];
|
newSize = row + 1;
|
||||||
System.arraycopy(oldRecords, 0, records, 0, oldRecords.length);
|
records = new CellValueRecordInterface[newSize][];
|
||||||
}
|
System.arraycopy(oldRecords, 0, records, 0, oldRecords.length);
|
||||||
CellValueRecordInterface[] rowCells = records[row];
|
}
|
||||||
if (rowCells == null) {
|
CellValueRecordInterface[] rowCells = records[row];
|
||||||
int newSize = column + 1;
|
if (rowCells == null) {
|
||||||
if(newSize<10) newSize=10;
|
int newSize = column + 1;
|
||||||
rowCells = new CellValueRecordInterface[newSize];
|
if (newSize < 10)
|
||||||
records[row] = rowCells;
|
newSize = 10;
|
||||||
}
|
rowCells = new CellValueRecordInterface[newSize];
|
||||||
if (column >= rowCells.length) {
|
records[row] = rowCells;
|
||||||
CellValueRecordInterface[] oldRowCells = rowCells;
|
}
|
||||||
int newSize = oldRowCells.length * 2;
|
if (column >= rowCells.length) {
|
||||||
if(newSize<column+1) newSize=column+1;
|
CellValueRecordInterface[] oldRowCells = rowCells;
|
||||||
// if(newSize>257) newSize=257; // activate?
|
int newSize = oldRowCells.length * 2;
|
||||||
rowCells = new CellValueRecordInterface[newSize];
|
if (newSize < column + 1)
|
||||||
System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length);
|
newSize = column + 1;
|
||||||
records[row] = rowCells;
|
// if(newSize>257) newSize=257; // activate?
|
||||||
}
|
rowCells = new CellValueRecordInterface[newSize];
|
||||||
rowCells[column] = cell;
|
System.arraycopy(oldRowCells, 0, rowCells, 0, oldRowCells.length);
|
||||||
|
records[row] = rowCells;
|
||||||
|
}
|
||||||
|
rowCells[column] = cell;
|
||||||
|
|
||||||
if ((column < firstcell) || (firstcell == -1)) {
|
if ((column < firstcell) || (firstcell == -1)) {
|
||||||
firstcell = column;
|
firstcell = column;
|
||||||
}
|
}
|
||||||
if ((column > lastcell) || (lastcell == -1)) {
|
if ((column > lastcell) || (lastcell == -1)) {
|
||||||
lastcell = column;
|
lastcell = column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeCell(CellValueRecordInterface cell) {
|
public void removeCell(CellValueRecordInterface cell) {
|
||||||
if (cell == null) {
|
if (cell == null) {
|
||||||
throw new IllegalArgumentException("cell must not be null");
|
throw new IllegalArgumentException("cell must not be null");
|
||||||
}
|
}
|
||||||
int row = cell.getRow();
|
int row = cell.getRow();
|
||||||
if (row >= records.length) {
|
if (row >= records.length) {
|
||||||
throw new RuntimeException("cell row is out of range");
|
throw new RuntimeException("cell row is out of range");
|
||||||
}
|
}
|
||||||
CellValueRecordInterface[] rowCells = records[row];
|
CellValueRecordInterface[] rowCells = records[row];
|
||||||
if (rowCells == null) {
|
if (rowCells == null) {
|
||||||
throw new RuntimeException("cell row is already empty");
|
throw new RuntimeException("cell row is already empty");
|
||||||
}
|
}
|
||||||
short column = cell.getColumn();
|
short column = cell.getColumn();
|
||||||
if (column >= rowCells.length) {
|
if (column >= rowCells.length) {
|
||||||
throw new RuntimeException("cell column is out of range");
|
throw new RuntimeException("cell column is out of range");
|
||||||
}
|
}
|
||||||
rowCells[column] = null;
|
rowCells[column] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAllCellsValuesForRow(int rowIndex) {
|
public void removeAllCellsValuesForRow(int rowIndex) {
|
||||||
if (rowIndex >= records.length) {
|
if (rowIndex < 0 || rowIndex > MAX_ROW_INDEX) {
|
||||||
throw new IllegalArgumentException("Specified rowIndex " + rowIndex
|
throw new IllegalArgumentException("Specified rowIndex " + rowIndex
|
||||||
+ " is outside the allowable range (0.." +records.length + ")");
|
+ " is outside the allowable range (0.." +MAX_ROW_INDEX + ")");
|
||||||
}
|
}
|
||||||
records[rowIndex] = null;
|
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()
|
public int getPhysicalNumberOfCells() {
|
||||||
{
|
int count = 0;
|
||||||
int count=0;
|
for (int r = 0; r < records.length; r++) {
|
||||||
for(int r=0;r<records.length;r++) {
|
CellValueRecordInterface[] rowCells = records[r];
|
||||||
CellValueRecordInterface[] rowCells=records[r];
|
if (rowCells != null) {
|
||||||
if (rowCells != null)
|
for (int c = 0; c < rowCells.length; c++) {
|
||||||
for(short c=0;c<rowCells.length;c++) {
|
if (rowCells[c] != null)
|
||||||
if(rowCells[c]!=null) count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
}
|
||||||
}
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
public int getFirstCellNum()
|
public int getFirstCellNum() {
|
||||||
{
|
return firstcell;
|
||||||
return firstcell;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int getLastCellNum()
|
public int getLastCellNum() {
|
||||||
{
|
return lastcell;
|
||||||
return lastcell;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a single cell value record
|
* Processes a single cell value record
|
||||||
* @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet
|
* @param sfh used to resolve any shared-formulas/arrays/tables for the current sheet
|
||||||
*/
|
*/
|
||||||
public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) {
|
public void construct(CellValueRecordInterface rec, RecordStream rs, SharedValueManager sfh) {
|
||||||
if (rec instanceof FormulaRecord) {
|
if (rec instanceof FormulaRecord) {
|
||||||
FormulaRecord formulaRec = (FormulaRecord)rec;
|
FormulaRecord formulaRec = (FormulaRecord)rec;
|
||||||
// read optional cached text value
|
// read optional cached text value
|
||||||
StringRecord cachedText;
|
StringRecord cachedText;
|
||||||
Class nextClass = rs.peekNextClass();
|
Class nextClass = rs.peekNextClass();
|
||||||
if (nextClass == StringRecord.class) {
|
if (nextClass == StringRecord.class) {
|
||||||
cachedText = (StringRecord) rs.getNext();
|
cachedText = (StringRecord) rs.getNext();
|
||||||
} else {
|
} else {
|
||||||
cachedText = null;
|
cachedText = null;
|
||||||
}
|
}
|
||||||
insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh));
|
insertCell(new FormulaRecordAggregate(formulaRec, cachedText, sfh));
|
||||||
} else {
|
} else {
|
||||||
insertCell(rec);
|
insertCell(rec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Tallies a count of the size of the cell records
|
/** Tallies a count of the size of the cell records
|
||||||
* that are attached to the rows in the range specified.
|
* that are attached to the rows in the range specified.
|
||||||
*/
|
*/
|
||||||
public int getRowCellBlockSize(int startRow, int endRow) {
|
public int getRowCellBlockSize(int startRow, int endRow) {
|
||||||
MyIterator itr = new MyIterator(startRow, endRow);
|
MyIterator itr = new MyIterator(records, startRow, endRow);
|
||||||
int size = 0;
|
int size = 0;
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
CellValueRecordInterface cell = (CellValueRecordInterface)itr.next();
|
CellValueRecordInterface cell = (CellValueRecordInterface) itr.next();
|
||||||
int row = cell.getRow();
|
int row = cell.getRow();
|
||||||
if (row > endRow)
|
if (row > endRow) {
|
||||||
break;
|
break;
|
||||||
if ((row >=startRow) && (row <= endRow))
|
}
|
||||||
size += ((RecordBase)cell).getRecordSize();
|
if ((row >= startRow) && (row <= endRow)) {
|
||||||
}
|
size += ((RecordBase) cell).getRecordSize();
|
||||||
return size;
|
}
|
||||||
}
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns true if the row has cells attached to it */
|
/** Returns true if the row has cells attached to it */
|
||||||
public boolean rowHasCells(int row) {
|
public boolean rowHasCells(int row) {
|
||||||
if (row > records.length-1) //previously this said row > records.length which means if
|
if (row >= records.length) {
|
||||||
return false; // if records.length == 60 and I pass "60" here I get array out of bounds
|
return false;
|
||||||
CellValueRecordInterface[] rowCells=records[row]; //because a 60 length array has the last index = 59
|
}
|
||||||
if(rowCells==null) return false;
|
CellValueRecordInterface[] rowCells=records[row];
|
||||||
for(int col=0;col<rowCells.length;col++) {
|
if(rowCells==null) return false;
|
||||||
if(rowCells[col]!=null) return true;
|
for(int col=0;col<rowCells.length;col++) {
|
||||||
}
|
if(rowCells[col]!=null) return true;
|
||||||
return false;
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Serializes the cells that are allocated to a certain row range*/
|
/** Serializes the cells that are allocated to a certain row range*/
|
||||||
public int serializeCellRow(final int row, int offset, byte [] data)
|
public int serializeCellRow(final int row, int offset, byte [] data)
|
||||||
{
|
{
|
||||||
MyIterator itr = new MyIterator(row, row);
|
MyIterator itr = new MyIterator(records, row, row);
|
||||||
int pos = offset;
|
int pos = offset;
|
||||||
|
|
||||||
while (itr.hasNext())
|
while (itr.hasNext())
|
||||||
{
|
{
|
||||||
CellValueRecordInterface cell = (CellValueRecordInterface)itr.next();
|
CellValueRecordInterface cell = (CellValueRecordInterface)itr.next();
|
||||||
if (cell.getRow() != row)
|
if (cell.getRow() != row)
|
||||||
break;
|
break;
|
||||||
pos += (( RecordBase ) cell).serialize(pos, data);
|
pos += (( RecordBase ) cell).serialize(pos, data);
|
||||||
}
|
}
|
||||||
return pos - offset;
|
return pos - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitCellsForRow(int rowIndex, RecordVisitor rv) {
|
public void visitCellsForRow(int rowIndex, RecordVisitor rv) {
|
||||||
|
|
||||||
CellValueRecordInterface[] cellRecs = records[rowIndex];
|
CellValueRecordInterface[] cellRecs = records[rowIndex];
|
||||||
if (cellRecs != null) {
|
if (cellRecs != null) {
|
||||||
for (int i = 0; i < cellRecs.length; i++) {
|
for (int i = 0; i < cellRecs.length; i++) {
|
||||||
CellValueRecordInterface cvr = cellRecs[i];
|
CellValueRecordInterface cvr = cellRecs[i];
|
||||||
if (cvr == null) {
|
if (cvr == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cvr instanceof RecordAggregate) {
|
if (cvr instanceof RecordAggregate) {
|
||||||
RecordAggregate agg = (RecordAggregate) cvr;
|
RecordAggregate agg = (RecordAggregate) cvr;
|
||||||
agg.visitContainedRecords(rv);
|
agg.visitContainedRecords(rv);
|
||||||
} else {
|
} else {
|
||||||
Record rec = (Record) cvr;
|
Record rec = (Record) cvr;
|
||||||
rv.visitRecord(rec);
|
rv.visitRecord(rec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
|
public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
|
||||||
for (int i = 0; i < records.length; i++) {
|
for (int i = 0; i < records.length; i++) {
|
||||||
CellValueRecordInterface[] rowCells = records[i];
|
CellValueRecordInterface[] rowCells = records[i];
|
||||||
if (rowCells == null) {
|
if (rowCells == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int j = 0; j < rowCells.length; j++) {
|
for (int j = 0; j < rowCells.length; j++) {
|
||||||
CellValueRecordInterface cell = rowCells[j];
|
CellValueRecordInterface cell = rowCells[j];
|
||||||
if (cell instanceof FormulaRecordAggregate) {
|
if (cell instanceof FormulaRecordAggregate) {
|
||||||
FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord();
|
FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord();
|
||||||
Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter?
|
Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter?
|
||||||
if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
|
if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
|
||||||
fr.setParsedExpression(ptgs);
|
fr.setParsedExpression(ptgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CellValueRecordInterface[] getValueRecords() {
|
public CellValueRecordInterface[] getValueRecords() {
|
||||||
List temp = new ArrayList();
|
List<CellValueRecordInterface> temp = new ArrayList<CellValueRecordInterface>();
|
||||||
|
|
||||||
for (int i = 0; i < records.length; i++) {
|
for (int i = 0; i < records.length; i++) {
|
||||||
CellValueRecordInterface[] rowCells = records[i];
|
CellValueRecordInterface[] rowCells = records[i];
|
||||||
if (rowCells == null) {
|
if (rowCells == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int j = 0; j < rowCells.length; j++) {
|
for (int j = 0; j < rowCells.length; j++) {
|
||||||
CellValueRecordInterface cell = rowCells[j];
|
CellValueRecordInterface cell = rowCells[j];
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
temp.add(cell);
|
temp.add(cell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()];
|
CellValueRecordInterface[] result = new CellValueRecordInterface[temp.size()];
|
||||||
temp.toArray(result);
|
temp.toArray(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public Iterator getIterator()
|
public Iterator getIterator() {
|
||||||
{
|
return new MyIterator(records);
|
||||||
return new MyIterator();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private final class MyIterator implements Iterator {
|
private static final class MyIterator implements Iterator {
|
||||||
short nextColumn=-1;
|
private final CellValueRecordInterface[][] records;
|
||||||
int nextRow,lastRow;
|
private short nextColumn = -1;
|
||||||
|
private int nextRow, lastRow;
|
||||||
|
|
||||||
public MyIterator()
|
public MyIterator(CellValueRecordInterface[][] pRecords) {
|
||||||
{
|
this(pRecords, 0, pRecords.length - 1);
|
||||||
this.nextRow=0;
|
}
|
||||||
this.lastRow=records.length-1;
|
|
||||||
findNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MyIterator(int firstRow,int lastRow)
|
public MyIterator(CellValueRecordInterface[][] pRecords, int firstRow, int lastRow) {
|
||||||
{
|
records = pRecords;
|
||||||
this.nextRow=firstRow;
|
this.nextRow = firstRow;
|
||||||
this.lastRow=lastRow;
|
this.lastRow = lastRow;
|
||||||
findNext();
|
findNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return nextRow<=lastRow;
|
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() {
|
public Object next() {
|
||||||
nextColumn++;
|
Object o = records[nextRow][nextColumn];
|
||||||
for(;nextRow<=lastRow;nextRow++) {
|
findNext();
|
||||||
//previously this threw array out of bounds...
|
return o;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,249 +37,282 @@ import org.apache.poi.hssf.record.Record;
|
|||||||
import org.apache.poi.hssf.record.RecordBase;
|
import org.apache.poi.hssf.record.RecordBase;
|
||||||
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
||||||
import org.apache.poi.hssf.record.WindowTwoRecord;
|
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.HSSFSheet;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ValueRecordsAggregate}
|
||||||
|
*/
|
||||||
public final class TestValueRecordsAggregate extends TestCase {
|
public final class TestValueRecordsAggregate extends TestCase {
|
||||||
private static final String ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE = "AbnormalSharedFormulaFlag.xls";
|
private static final String ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE = "AbnormalSharedFormulaFlag.xls";
|
||||||
private final ValueRecordsAggregate valueRecord = new ValueRecordsAggregate();
|
private final ValueRecordsAggregate valueRecord = new ValueRecordsAggregate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed
|
* Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed
|
||||||
* as part of the value records
|
* as part of the value records
|
||||||
*/
|
*/
|
||||||
public void testSharedFormula() {
|
public void testSharedFormula() {
|
||||||
List records = new ArrayList();
|
List<Record> records = new ArrayList<Record>();
|
||||||
records.add( new FormulaRecord() );
|
records.add(new FormulaRecord());
|
||||||
records.add( new SharedFormulaRecord() );
|
records.add(new SharedFormulaRecord());
|
||||||
records.add(new WindowTwoRecord());
|
records.add(new WindowTwoRecord());
|
||||||
|
|
||||||
constructValueRecord(records);
|
constructValueRecord(records);
|
||||||
Iterator iterator = valueRecord.getIterator();
|
Iterator iterator = valueRecord.getIterator();
|
||||||
RecordBase record = (RecordBase) iterator.next();
|
RecordBase record = (RecordBase) iterator.next();
|
||||||
assertNotNull( "Row contains a value", record );
|
assertNotNull( "Row contains a value", record );
|
||||||
assertTrue( "First record is a FormulaRecordsAggregate", ( record instanceof FormulaRecordAggregate ) );
|
assertTrue( "First record is a FormulaRecordsAggregate", ( record instanceof FormulaRecordAggregate ) );
|
||||||
//Ensure that the SharedFormulaRecord has been converted
|
//Ensure that the SharedFormulaRecord has been converted
|
||||||
assertFalse( "SharedFormulaRecord is null", iterator.hasNext() );
|
assertFalse( "SharedFormulaRecord is null", iterator.hasNext() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void constructValueRecord(List records) {
|
private void constructValueRecord(List records) {
|
||||||
RowBlocksReader rbr = new RowBlocksReader(new RecordStream(records, 0));
|
RowBlocksReader rbr = new RowBlocksReader(new RecordStream(records, 0));
|
||||||
SharedValueManager sfrh = rbr.getSharedFormulaManager();
|
SharedValueManager sfrh = rbr.getSharedFormulaManager();
|
||||||
RecordStream rs = rbr.getPlainRecordStream();
|
RecordStream rs = rbr.getPlainRecordStream();
|
||||||
while(rs.hasNext()) {
|
while(rs.hasNext()) {
|
||||||
Record rec = rs.getNext();
|
Record rec = rs.getNext();
|
||||||
valueRecord.construct((CellValueRecordInterface)rec, rs, sfrh);
|
valueRecord.construct((CellValueRecordInterface)rec, rs, sfrh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List testData() {
|
private static List testData() {
|
||||||
List records = new ArrayList();
|
List<Record> records = new ArrayList<Record>();
|
||||||
FormulaRecord formulaRecord = new FormulaRecord();
|
FormulaRecord formulaRecord = new FormulaRecord();
|
||||||
BlankRecord blankRecord = new BlankRecord();
|
BlankRecord blankRecord = new BlankRecord();
|
||||||
formulaRecord.setRow( 1 );
|
formulaRecord.setRow(1);
|
||||||
formulaRecord.setColumn( (short) 1 );
|
formulaRecord.setColumn((short) 1);
|
||||||
blankRecord.setRow( 2 );
|
blankRecord.setRow(2);
|
||||||
blankRecord.setColumn( (short) 2 );
|
blankRecord.setColumn((short) 2);
|
||||||
records.add( formulaRecord );
|
records.add(formulaRecord);
|
||||||
records.add( blankRecord );
|
records.add(blankRecord);
|
||||||
records.add(new WindowTwoRecord());
|
records.add(new WindowTwoRecord());
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInsertCell() {
|
public void testInsertCell() {
|
||||||
Iterator iterator = valueRecord.getIterator();
|
Iterator iterator = valueRecord.getIterator();
|
||||||
assertFalse( iterator.hasNext() );
|
assertFalse( iterator.hasNext() );
|
||||||
|
|
||||||
BlankRecord blankRecord = newBlankRecord();
|
BlankRecord blankRecord = newBlankRecord();
|
||||||
valueRecord.insertCell( blankRecord );
|
valueRecord.insertCell( blankRecord );
|
||||||
iterator = valueRecord.getIterator();
|
iterator = valueRecord.getIterator();
|
||||||
assertTrue( iterator.hasNext() );
|
assertTrue( iterator.hasNext() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRemoveCell() {
|
public void testRemoveCell() {
|
||||||
BlankRecord blankRecord1 = newBlankRecord();
|
BlankRecord blankRecord1 = newBlankRecord();
|
||||||
valueRecord.insertCell( blankRecord1 );
|
valueRecord.insertCell( blankRecord1 );
|
||||||
BlankRecord blankRecord2 = newBlankRecord();
|
BlankRecord blankRecord2 = newBlankRecord();
|
||||||
valueRecord.removeCell( blankRecord2 );
|
valueRecord.removeCell( blankRecord2 );
|
||||||
Iterator iterator = valueRecord.getIterator();
|
Iterator iterator = valueRecord.getIterator();
|
||||||
assertFalse( iterator.hasNext() );
|
assertFalse( iterator.hasNext() );
|
||||||
|
|
||||||
// removing an already empty cell just falls through
|
// removing an already empty cell just falls through
|
||||||
valueRecord.removeCell( blankRecord2 );
|
valueRecord.removeCell( blankRecord2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetPhysicalNumberOfCells() {
|
public void testGetPhysicalNumberOfCells() {
|
||||||
assertEquals(0, valueRecord.getPhysicalNumberOfCells());
|
assertEquals(0, valueRecord.getPhysicalNumberOfCells());
|
||||||
BlankRecord blankRecord1 = newBlankRecord();
|
BlankRecord blankRecord1 = newBlankRecord();
|
||||||
valueRecord.insertCell( blankRecord1 );
|
valueRecord.insertCell( blankRecord1 );
|
||||||
assertEquals(1, valueRecord.getPhysicalNumberOfCells());
|
assertEquals(1, valueRecord.getPhysicalNumberOfCells());
|
||||||
valueRecord.removeCell( blankRecord1 );
|
valueRecord.removeCell( blankRecord1 );
|
||||||
assertEquals(0, valueRecord.getPhysicalNumberOfCells());
|
assertEquals(0, valueRecord.getPhysicalNumberOfCells());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetFirstCellNum() {
|
public void testGetFirstCellNum() {
|
||||||
assertEquals( -1, valueRecord.getFirstCellNum() );
|
assertEquals( -1, valueRecord.getFirstCellNum() );
|
||||||
valueRecord.insertCell( newBlankRecord( 2, 2 ) );
|
valueRecord.insertCell( newBlankRecord( 2, 2 ) );
|
||||||
assertEquals( 2, valueRecord.getFirstCellNum() );
|
assertEquals( 2, valueRecord.getFirstCellNum() );
|
||||||
valueRecord.insertCell( newBlankRecord( 3, 3 ) );
|
valueRecord.insertCell( newBlankRecord( 3, 3 ) );
|
||||||
assertEquals( 2, valueRecord.getFirstCellNum() );
|
assertEquals( 2, valueRecord.getFirstCellNum() );
|
||||||
|
|
||||||
// Note: Removal doesn't currently reset the first column. It probably should but it doesn't.
|
// Note: Removal doesn't currently reset the first column. It probably should but it doesn't.
|
||||||
valueRecord.removeCell( newBlankRecord( 2, 2 ) );
|
valueRecord.removeCell( newBlankRecord( 2, 2 ) );
|
||||||
assertEquals( 2, valueRecord.getFirstCellNum() );
|
assertEquals( 2, valueRecord.getFirstCellNum() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetLastCellNum() {
|
public void testGetLastCellNum() {
|
||||||
assertEquals( -1, valueRecord.getLastCellNum() );
|
assertEquals( -1, valueRecord.getLastCellNum() );
|
||||||
valueRecord.insertCell( newBlankRecord( 2, 2 ) );
|
valueRecord.insertCell( newBlankRecord( 2, 2 ) );
|
||||||
assertEquals( 2, valueRecord.getLastCellNum() );
|
assertEquals( 2, valueRecord.getLastCellNum() );
|
||||||
valueRecord.insertCell( newBlankRecord( 3, 3 ) );
|
valueRecord.insertCell( newBlankRecord( 3, 3 ) );
|
||||||
assertEquals( 3, valueRecord.getLastCellNum() );
|
assertEquals( 3, valueRecord.getLastCellNum() );
|
||||||
|
|
||||||
// Note: Removal doesn't currently reset the last column. It probably should but it doesn't.
|
// Note: Removal doesn't currently reset the last column. It probably should but it doesn't.
|
||||||
valueRecord.removeCell( newBlankRecord( 3, 3 ) );
|
valueRecord.removeCell( newBlankRecord( 3, 3 ) );
|
||||||
assertEquals( 3, valueRecord.getLastCellNum() );
|
assertEquals( 3, valueRecord.getLastCellNum() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSerialize() {
|
public void testSerialize() {
|
||||||
byte[] actualArray = new byte[36];
|
byte[] actualArray = new byte[36];
|
||||||
byte[] expectedArray = new byte[]
|
byte[] expectedArray = new byte[]
|
||||||
{
|
{
|
||||||
(byte)0x06, (byte)0x00, (byte)0x16, (byte)0x00,
|
(byte)0x06, (byte)0x00, (byte)0x16, (byte)0x00,
|
||||||
(byte)0x01, (byte)0x00, (byte)0x01, (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)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)0x00, (byte)0x00, (byte)0x01, (byte)0x02,
|
||||||
(byte)0x06, (byte)0x00, (byte)0x02, (byte)0x00,
|
(byte)0x06, (byte)0x00, (byte)0x02, (byte)0x00,
|
||||||
(byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00,
|
(byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||||
};
|
};
|
||||||
List records = testData();
|
List records = testData();
|
||||||
constructValueRecord(records);
|
constructValueRecord(records);
|
||||||
int bytesWritten = valueRecord.serializeCellRow(1, 0, actualArray );
|
int bytesWritten = valueRecord.serializeCellRow(1, 0, actualArray );
|
||||||
bytesWritten += valueRecord.serializeCellRow(2, bytesWritten, actualArray );
|
bytesWritten += valueRecord.serializeCellRow(2, bytesWritten, actualArray );
|
||||||
assertEquals( 36, bytesWritten );
|
assertEquals( 36, bytesWritten );
|
||||||
for (int i = 0; i < 36; i++)
|
for (int i = 0; i < 36; i++)
|
||||||
assertEquals( expectedArray[i], actualArray[i] );
|
assertEquals( expectedArray[i], actualArray[i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlankRecord newBlankRecord()
|
private static BlankRecord newBlankRecord()
|
||||||
{
|
{
|
||||||
return newBlankRecord( 2, 2 );
|
return newBlankRecord( 2, 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BlankRecord newBlankRecord( int col, int row)
|
private static BlankRecord newBlankRecord( int col, int row)
|
||||||
{
|
{
|
||||||
BlankRecord blankRecord = new BlankRecord();
|
BlankRecord blankRecord = new BlankRecord();
|
||||||
blankRecord.setRow( row );
|
blankRecord.setRow( row );
|
||||||
blankRecord.setColumn( (short) col );
|
blankRecord.setColumn( (short) col );
|
||||||
return blankRecord;
|
return blankRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sometimes the 'shared formula' flag (<tt>FormulaRecord.isSharedFormula()</tt>) is set when
|
* Sometimes the 'shared formula' flag (<tt>FormulaRecord.isSharedFormula()</tt>) is set when
|
||||||
* there is no corresponding SharedFormulaRecord available. SharedFormulaRecord definitions do
|
* 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
|
* 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/>
|
* 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
|
* So it is important that the code which locates the SharedFormulaRecord for each
|
||||||
* FormulaRecord does not allow matches across sheets.</br>
|
* FormulaRecord does not allow matches across sheets.</br>
|
||||||
*
|
*
|
||||||
* Prior to bugzilla 44449 (Feb 2008), POI <tt>ValueRecordsAggregate.construct(int, List)</tt>
|
* 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
|
* allowed <tt>SharedFormulaRecord</tt>s to be erroneously used across sheets. That incorrect
|
||||||
* behaviour is shown by this test.<p/>
|
* behaviour is shown by this test.<p/>
|
||||||
*
|
*
|
||||||
* <b>Notes on how to produce the test spreadsheet</b>:</p>
|
* <b>Notes on how to produce the test spreadsheet</b>:</p>
|
||||||
* The setup for this test (AbnormalSharedFormulaFlag.xls) is rather fragile, insomuchas
|
* The setup for this test (AbnormalSharedFormulaFlag.xls) is rather fragile, insomuchas
|
||||||
* re-saving the file (either with Excel or POI) clears the flag.<br/>
|
* re-saving the file (either with Excel or POI) clears the flag.<br/>
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>A new spreadsheet was created in Excel (File | New | Blank Workbook).</li>
|
* <li>A new spreadsheet was created in Excel (File | New | Blank Workbook).</li>
|
||||||
* <li>Sheet3 was deleted.</li>
|
* <li>Sheet3 was deleted.</li>
|
||||||
* <li>Sheet2!A1 formula was set to '="second formula"', and fill-dragged through A1:A8.</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>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>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>
|
* <li>The spreadsheet was saved as AbnormalSharedFormulaFlag.xls.</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* Prior to the row delete action the spreadsheet has two <tt>SharedFormulaRecord</tt>s. One
|
* 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/>
|
* 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
|
* 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/>
|
* <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.
|
* There are other variations on this theme to create the same effect.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void testSpuriousSharedFormulaFlag() {
|
public void testSpuriousSharedFormulaFlag() {
|
||||||
|
|
||||||
long actualCRC = getFileCRC(HSSFTestDataSamples.openSampleFileStream(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE));
|
long actualCRC = getFileCRC(HSSFTestDataSamples.openSampleFileStream(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE));
|
||||||
long expectedCRC = 2277445406L;
|
long expectedCRC = 2277445406L;
|
||||||
if(actualCRC != expectedCRC) {
|
if(actualCRC != expectedCRC) {
|
||||||
System.err.println("Expected crc " + expectedCRC + " but got " + actualCRC);
|
System.err.println("Expected crc " + expectedCRC + " but got " + actualCRC);
|
||||||
throw failUnexpectedTestFileChange();
|
throw failUnexpectedTestFileChange();
|
||||||
}
|
}
|
||||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE);
|
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE);
|
||||||
|
|
||||||
HSSFSheet s = wb.getSheetAt(0); // Sheet1
|
HSSFSheet s = wb.getSheetAt(0); // Sheet1
|
||||||
|
|
||||||
String cellFormula;
|
String cellFormula;
|
||||||
cellFormula = getFormulaFromFirstCell(s, 0); // row "1"
|
cellFormula = getFormulaFromFirstCell(s, 0); // row "1"
|
||||||
// the problem is not observable in the first row of the shared formula
|
// the problem is not observable in the first row of the shared formula
|
||||||
if(!cellFormula.equals("\"first formula\"")) {
|
if(!cellFormula.equals("\"first formula\"")) {
|
||||||
throw new RuntimeException("Something else wrong with this test case");
|
throw new RuntimeException("Something else wrong with this test case");
|
||||||
}
|
}
|
||||||
|
|
||||||
// but the problem is observable in rows 2,3,4
|
// but the problem is observable in rows 2,3,4
|
||||||
cellFormula = getFormulaFromFirstCell(s, 1); // row "2"
|
cellFormula = getFormulaFromFirstCell(s, 1); // row "2"
|
||||||
if(cellFormula.equals("\"second formula\"")) {
|
if(cellFormula.equals("\"second formula\"")) {
|
||||||
throw new AssertionFailedError("found bug 44449 (Wrong SharedFormulaRecord was used).");
|
throw new AssertionFailedError("found bug 44449 (Wrong SharedFormulaRecord was used).");
|
||||||
}
|
}
|
||||||
if(!cellFormula.equals("\"first formula\"")) {
|
if(!cellFormula.equals("\"first formula\"")) {
|
||||||
throw new RuntimeException("Something else wrong with this test case");
|
throw new RuntimeException("Something else wrong with this test case");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static String getFormulaFromFirstCell(HSSFSheet s, int rowIx) {
|
private static String getFormulaFromFirstCell(HSSFSheet s, int rowIx) {
|
||||||
return s.getRow(rowIx).getCell(0).getCellFormula();
|
return s.getRow(rowIx).getCell(0).getCellFormula();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If someone opened this particular test file in Excel and saved it, the peculiar condition
|
* 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
|
* 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
|
* regardless of whether the fix was present. So a CRC check is performed to make it less easy
|
||||||
* for that to occur.
|
* for that to occur.
|
||||||
*/
|
*/
|
||||||
private static RuntimeException failUnexpectedTestFileChange() {
|
private static RuntimeException failUnexpectedTestFileChange() {
|
||||||
String msg = "Test file '" + ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE + "' has changed. "
|
String msg = "Test file '" + ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE + "' has changed. "
|
||||||
+ "This junit may not be properly testing for the target bug. "
|
+ "This junit may not be properly testing for the target bug. "
|
||||||
+ "Either revert the test file or ensure that the new version "
|
+ "Either revert the test file or ensure that the new version "
|
||||||
+ "has the right characteristics to test the target bug.";
|
+ "has the right characteristics to test the target bug.";
|
||||||
// A breakpoint in ValueRecordsAggregate.handleMissingSharedFormulaRecord(FormulaRecord)
|
// A breakpoint in ValueRecordsAggregate.handleMissingSharedFormulaRecord(FormulaRecord)
|
||||||
// should get hit during parsing of Sheet1.
|
// should get hit during parsing of Sheet1.
|
||||||
// If the test spreadsheet is created as directed, this condition should occur.
|
// 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
|
// It is easy to upset the test spreadsheet (for example re-saving will destroy the
|
||||||
// peculiar condition we are testing for).
|
// peculiar condition we are testing for).
|
||||||
throw new RuntimeException(msg);
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets a CRC checksum for the content of a file
|
* gets a CRC checksum for the content of a file
|
||||||
*/
|
*/
|
||||||
private static long getFileCRC(InputStream is) {
|
private static long getFileCRC(InputStream is) {
|
||||||
CRC32 crc = new CRC32();
|
CRC32 crc = new CRC32();
|
||||||
byte[] buf = new byte[2048];
|
byte[] buf = new byte[2048];
|
||||||
try {
|
try {
|
||||||
while(true) {
|
while(true) {
|
||||||
int bytesRead = is.read(buf);
|
int bytesRead = is.read(buf);
|
||||||
if(bytesRead < 1) {
|
if(bytesRead < 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
crc.update(buf, 0, bytesRead);
|
crc.update(buf, 0, bytesRead);
|
||||||
}
|
}
|
||||||
is.close();
|
is.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return crc.getValue();
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user