]> source.dussan.org Git - poi.git/commitdiff
Patch from Josh from bug #43901 - Correctly update the internal last cell number...
authorNick Burch <nick@apache.org>
Fri, 7 Mar 2008 11:18:02 +0000 (11:18 +0000)
committerNick Burch <nick@apache.org>
Fri, 7 Mar 2008 11:18:02 +0000 (11:18 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@634617 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFRow.java

index 99d81b0aecb9c3503bba82bbdd9d7275f1a3f6dc..bd4f7dab037064e97eb3821b1b66627851f31553 100644 (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>
index 6fcbfee45c78ab0d863d7120c1afdc84572a0055..62d0a8dceb355c1a9f2e5764c7628d2cb0ef7d3b 100644 (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>
index c759bcb46b02847ad42d0a07345ae8115fe72229..30bdc01f513375ddacb2bff4ba78e80a4abfb30c 100644 (file)
    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;
       }
       
index 0c48dfad04d8de6c9bc8d645ebf38660b58a2e9a..04c729b3be8c40dd6ce42c357facfa125ad29fea 100644 (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());
-       }
 }
 
 
index 44c03cd20de857e908b4204d41cca9183e00a583..b6f22022c67b7c5b80b5ec3da9d62b6e009bd99e 100644 (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
    See the License for the specific language governing permissions and
    limitations under the License.
 ==================================================================== */
-        
 
 package org.apache.poi.hssf.usermodel;
 
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 
-import org.apache.poi.util.TempFile;
+import junit.framework.TestCase;
 
 /**
  * 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());
     }
-    
 }