Patch from Josh from bug #43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@634617 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-03-07 11:18:02 +00:00
parent ba4e8c0a9f
commit e2dd40f66c
5 changed files with 164 additions and 176 deletions

View File

@ -36,6 +36,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.1-beta1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one)</action>
<action dev="POI-DEVELOPERS" type="fix">28231 - For apparently truncated files, which are somehow still valid, now issue a truncation warning but carry on, rather than giving an exception as before</action>
<action dev="POI-DEVELOPERS" type="fix">44504 - Added initial support for recognising external functions like YEARFRAC and ISEVEN (using NameXPtg), via LinkTable support</action>
<action dev="POI-DEVELOPERS" type="fix">44504 - Improvements to FormulaParser - operators, precedence, error literals, quotes in string literals, range checking on IntPtg, formulas with extra un-parsed stuff at the end, improved parse error handling</action>

View File

@ -33,6 +33,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.1-beta1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one)</action>
<action dev="POI-DEVELOPERS" type="fix">44504 - Added initial support for recognising external functions like YEARFRAC and ISEVEN (using NameXPtg), via LinkTable support</action>
<action dev="POI-DEVELOPERS" type="fix">44504 - Improvements to FormulaParser - operators, precedence, error literals, quotes in string literals, range checking on IntPtg, formulas with extra un-parsed stuff at the end, improved parse error handling</action>
<action dev="POI-DEVELOPERS" type="fix">44504 - Fixed number conversion inconsistencies in many functions, and improved RefEval</action>

View File

@ -15,11 +15,6 @@
limitations under the License.
==================================================================== */
/*
* HSSFRow.java
*
* Created on September 30, 2001, 3:44 PM
*/
package org.apache.poi.hssf.usermodel;
import java.util.Iterator;
@ -38,10 +33,7 @@ import org.apache.poi.hssf.record.RowRecord;
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
*/
public class HSSFRow
implements Comparable
{
public final class HSSFRow implements Comparable {
// used for collections
public final static int INITIAL_CAPACITY = 5;
@ -157,26 +149,31 @@ public class HSSFRow
* @param cell to remove
*/
public void removeCell(HSSFCell cell) {
removeCell(cell, true);
if(cell == null) {
throw new IllegalArgumentException("cell must not be null");
}
removeCell(cell, true);
}
private void removeCell(HSSFCell cell, boolean alsoRemoveRecords) {
if(alsoRemoveRecords) {
CellValueRecordInterface cval = cell.getCellValueRecord();
sheet.removeValueRecord(getRowNum(), cval);
}
short column=cell.getCellNum();
if(cell!=null && column<cells.length)
{
cells[column]=null;
if(column < 0) {
throw new RuntimeException("Negative cell indexes not allowed");
}
if (cell.getCellNum() == row.getLastCol())
{
row.setLastCol(findLastCell(row.getLastCol()));
if(column >= cells.length || cell != cells[column]) {
throw new RuntimeException("Specified cell is not from this row");
}
if (cell.getCellNum() == row.getFirstCol())
{
cells[column]=null;
if(alsoRemoveRecords) {
CellValueRecordInterface cval = cell.getCellValueRecord();
sheet.removeValueRecord(getRowNum(), cval);
}
if (cell.getCellNum()+1 == row.getLastCol()) {
row.setLastCol((short) (findLastCell(row.getLastCol())+1));
}
if (cell.getCellNum() == row.getFirstCol()) {
row.setFirstCol(findFirstCell(row.getFirstCol()));
}
}
@ -234,7 +231,7 @@ public class HSSFRow
* TODO - Should this really be public?
*/
protected int getOutlineLevel() {
return row.getOutlineLevel();
return row.getOutlineLevel();
}
/**
@ -244,55 +241,48 @@ public class HSSFRow
* @param newColumn The new column number (0 based)
*/
public void moveCell(HSSFCell cell, short newColumn) {
// Ensure the destination is free
if(cells.length > newColumn && cells[newColumn] != null) {
throw new IllegalArgumentException("Asked to move cell to column " + newColumn + " but there's already a cell there");
}
// Check it's one of ours
if(! cells[cell.getCellNum()].equals(cell)) {
throw new IllegalArgumentException("Asked to move a cell, but it didn't belong to our row");
}
// Move the cell to the new position
// (Don't remove the records though)
removeCell(cell, false);
cell.updateCellNum(newColumn);
addCell(cell);
// Ensure the destination is free
if(cells.length > newColumn && cells[newColumn] != null) {
throw new IllegalArgumentException("Asked to move cell to column " + newColumn + " but there's already a cell there");
}
// Check it's one of ours
if(! cells[cell.getCellNum()].equals(cell)) {
throw new IllegalArgumentException("Asked to move a cell, but it didn't belong to our row");
}
// Move the cell to the new position
// (Don't remove the records though)
removeCell(cell, false);
cell.updateCellNum(newColumn);
addCell(cell);
}
/**
* used internally to add a cell.
*/
private void addCell(HSSFCell cell)
{
short column=cell.getCellNum();
if (row.getFirstCol() == -1)
{
row.setFirstCol(column);
}
if (row.getLastCol() == -1)
{
row.setLastCol(column);
}
private void addCell(HSSFCell cell) {
if(column>=cells.length)
{
HSSFCell[] oldCells=cells;
int newSize=oldCells.length*2;
if(newSize<column+1) newSize=column+1;
cells=new HSSFCell[newSize];
System.arraycopy(oldCells,0,cells,0,oldCells.length);
short column=cell.getCellNum();
// re-allocate cells array as required.
if(column>=cells.length) {
HSSFCell[] oldCells=cells;
int newSize=oldCells.length*2;
if(newSize<column+1) {
newSize=column+1;
}
cells=new HSSFCell[newSize];
System.arraycopy(oldCells,0,cells,0,oldCells.length);
}
cells[column]=cell;
if (column < row.getFirstCol())
{
// fix up firstCol and lastCol indexes
if (row.getFirstCol() == -1 || column < row.getFirstCol()) {
row.setFirstCol(column);
}
if (column > row.getLastCol())
{
row.setLastCol(column);
if (row.getLastCol() == -1 || column >= row.getLastCol()) {
row.setLastCol((short) (column+1)); // +1 -> for one past the last index
}
}
@ -324,16 +314,29 @@ public class HSSFRow
}
/**
* gets the number of the last cell contained in this row <b>PLUS ONE</b>.
* @return short representing the last logical cell in the row <b>PLUS ONE</b>, or -1 if the row does not contain any cells.
* Gets the index of the last cell contained in this row <b>PLUS ONE</b>. The result also
* happens to be the 1-based column number of the last cell. This value can be used as a
* standard upper bound when iterating over cells:
* <pre>
* short minColIx = row.getFirstCellNum();
* short maxColIx = row.getLastCellNum();
* for(short colIx=minColIx; colIx&lt;maxColIx; colIx++) {
* HSSFCell cell = row.getCell(colIx);
* if(cell == null) {
* continue;
* }
* //... do something with cell
* }
* </pre>
*
* @return short representing the last logical cell in the row <b>PLUS ONE</b>, or -1 if the
* row does not contain any cells.
*/
public short getLastCellNum()
{
if (getPhysicalNumberOfCells() == 0)
public short getLastCellNum() {
if (getPhysicalNumberOfCells() == 0) {
return -1;
else
return row.getLastCol();
}
return row.getLastCol();
}
@ -493,8 +496,8 @@ public class HSSFRow
}
public Object next() {
if (!hasNext())
throw new NoSuchElementException("At last element");
if (!hasNext())
throw new NoSuchElementException("At last element");
HSSFCell cell=cells[nextId];
thisId=nextId;
findNext();
@ -502,8 +505,8 @@ public class HSSFRow
}
public void remove() {
if (thisId == -1)
throw new IllegalStateException("remove() called before next()");
if (thisId == -1)
throw new IllegalStateException("remove() called before next()");
cells[thisId]=null;
}

View File

@ -1104,29 +1104,6 @@ extends TestCase {
assertEquals(1, wb.getNumberOfSheets());
}
/**
* POI is producing files with the wrong last-column
* number on the row
*/
public void BROKENtest43901() {
HSSFWorkbook book = new HSSFWorkbook();
HSSFSheet sheet = book.createSheet("test");
// New row has last col -1
HSSFRow row = sheet.createRow(0);
assertEquals(-1, row.getLastCellNum());
if(row.getLastCellNum() == 0) {
fail("Identified bug 43901");
}
// Create two cells, will return one higher
// than that for the last number
row.createCell((short) 0);
assertEquals(1, row.getLastCellNum());
row.createCell((short) 255);
assertEquals(256, row.getLastCellNum());
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -15,34 +14,22 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import junit.framework.TestCase;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.util.TempFile;
/**
* Test HSSFRow is okay.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class TestHSSFRow
extends TestCase
{
public TestHSSFRow(String s)
{
super(s);
}
public final class TestHSSFRow extends TestCase {
public void testLastAndFirstColumns()
throws Exception
{
public void testLastAndFirstColumns() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
HSSFRow row = sheet.createRow((short) 0);
@ -51,127 +38,146 @@ public class TestHSSFRow
row.createCell((short) 2);
assertEquals(2, row.getFirstCellNum());
assertEquals(2, row.getLastCellNum());
assertEquals(3, row.getLastCellNum());
row.createCell((short) 1);
assertEquals(1, row.getFirstCellNum());
assertEquals(2, row.getLastCellNum());
assertEquals(3, row.getLastCellNum());
// check the exact case reported in 'bug' 43901 - notice that the cellNum is '0' based
row.createCell((short) 3);
assertEquals(1, row.getFirstCellNum());
assertEquals(3, row.getLastCellNum());
assertEquals(4, row.getLastCellNum());
}
public void testRemoveCell()
throws Exception
{
public void testRemoveCell() throws Exception {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
HSSFRow row = sheet.createRow((short) 0);
assertEquals(-1, row.getLastCellNum());
assertEquals(-1, row.getFirstCellNum());
row.createCell((short) 1);
assertEquals(1, row.getLastCellNum());
assertEquals(2, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
row.createCell((short) 3);
assertEquals(3, row.getLastCellNum());
assertEquals(4, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
row.removeCell(row.getCell((short) 3));
assertEquals(1, row.getLastCellNum());
assertEquals(2, row.getLastCellNum());
assertEquals(1, row.getFirstCellNum());
row.removeCell(row.getCell((short) 1));
assertEquals(-1, row.getLastCellNum());
assertEquals(-1, row.getFirstCellNum());
// check the row record actually writes it out as 0's
// all cells on this row have been removed
// so check the row record actually writes it out as 0's
byte[] data = new byte[100];
row.getRowRecord().serialize(0, data);
assertEquals(0, data[6]);
assertEquals(0, data[8]);
File file = TempFile.createTempFile("XXX", "XLS");
FileOutputStream stream = new FileOutputStream(file);
workbook.write(stream);
stream.close();
FileInputStream inputStream = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
baos.close();
ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
workbook = new HSSFWorkbook(inputStream);
sheet = workbook.getSheetAt(0);
stream.close();
file.delete();
inputStream.close();
assertEquals(-1, sheet.getRow((short) 0).getLastCellNum());
assertEquals(-1, sheet.getRow((short) 0).getFirstCellNum());
}
public void testMoveCell() throws Exception {
public void testMoveCell() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
HSSFRow row = sheet.createRow((short) 0);
HSSFRow rowB = sheet.createRow((short) 1);
HSSFCell cellA2 = rowB.createCell((short)0);
assertEquals(0, rowB.getFirstCellNum());
assertEquals(0, rowB.getFirstCellNum());
assertEquals(-1, row.getLastCellNum());
assertEquals(-1, row.getFirstCellNum());
HSSFCell cellB2 = row.createCell((short) 1);
HSSFCell cellB3 = row.createCell((short) 2);
HSSFCell cellB4 = row.createCell((short) 3);
assertEquals(1, row.getFirstCellNum());
assertEquals(3, row.getLastCellNum());
assertEquals(4, row.getLastCellNum());
// Try to move to somewhere else that's used
try {
row.moveCell(cellB2, (short)3);
fail();
} catch(IllegalArgumentException e) {}
row.moveCell(cellB2, (short)3);
fail("IllegalArgumentException should have been thrown");
} catch(IllegalArgumentException e) {
// expected during successful test
}
// Try to move one off a different row
try {
row.moveCell(cellA2, (short)3);
fail();
} catch(IllegalArgumentException e) {}
row.moveCell(cellA2, (short)3);
fail("IllegalArgumentException should have been thrown");
} catch(IllegalArgumentException e) {
// expected during successful test
}
// Move somewhere spare
assertNotNull(row.getCell((short)1));
row.moveCell(cellB2, (short)5);
row.moveCell(cellB2, (short)5);
assertNull(row.getCell((short)1));
assertNotNull(row.getCell((short)5));
assertEquals(5, cellB2.getCellNum());
assertEquals(5, cellB2.getCellNum());
assertEquals(2, row.getFirstCellNum());
assertEquals(5, row.getLastCellNum());
assertEquals(6, row.getLastCellNum());
}
public void testRowBounds()
throws Exception
{
public void testRowBounds() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
//Test low row bound
HSSFRow row = sheet.createRow( (short) 0);
//Test low row bound exception
boolean caughtException = false;
sheet.createRow( (short) 0);
//Test low row bound exception
try {
row = sheet.createRow(-1);
sheet.createRow(-1);
fail("IndexOutOfBoundsException should have been thrown");
} catch (IndexOutOfBoundsException ex) {
caughtException = true;
}
assertTrue(caughtException);
//Test high row bound
row = sheet.createRow(65535);
//Test high row bound exception
caughtException = false;
// expected during successful test
}
//Test high row bound
sheet.createRow(65535);
//Test high row bound exception
try {
row = sheet.createRow(65536);
sheet.createRow(65536);
fail("IndexOutOfBoundsException should have been thrown");
} catch (IndexOutOfBoundsException ex) {
caughtException = true;
}
assertTrue(caughtException);
// expected during successful test
}
}
/**
* Prior to patch 43901, POI was producing files with the wrong last-column
* number on the row
*/
public void testLastCellNumIsCorrectAfterAddCell_bug43901(){
HSSFWorkbook book = new HSSFWorkbook();
HSSFSheet sheet = book.createSheet("test");
HSSFRow row = sheet.createRow(0);
// New row has last col -1
assertEquals(-1, row.getLastCellNum());
if(row.getLastCellNum() == 0) {
fail("Identified bug 43901");
}
// Create two cells, will return one higher
// than that for the last number
row.createCell((short) 0);
assertEquals(1, row.getLastCellNum());
row.createCell((short) 255);
assertEquals(256, row.getLastCellNum());
}
}