]> source.dussan.org Git - poi.git/commitdiff
Fix for bug 46312 - ValueRecordsAggregate should handle removal of new empty row
authorJosh Micich <josh@apache.org>
Mon, 1 Dec 2008 19:59:46 +0000 (19:59 +0000)
committerJosh Micich <josh@apache.org>
Mon, 1 Dec 2008 19:59:46 +0000 (19:59 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@722206 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java

index 991bd0ec160a4257e6f913ce7ecf03fdc9fd5c49..80a6e8cee56f63f2def4fb1415b18cd92d7ea5b1 100644 (file)
@@ -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>
index 1a1fd30ebe76f7204a5fcd36b3273dc52aac4aff..8763adc0a033199e8775fe88887ed0175df7166f 100644 (file)
@@ -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>
index 27000811ce8de6b25e8763c078fd4906c390c460..6ee871a7f2ed57735e37ad80ec99607b17e580a0 100644 (file)
@@ -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;
+                       }
+               }
+
+       }
 }
index c62f7252386a5974a236912da7c6af5f7dc132cc..af119f29b924652560fce5c79290e95940688a10 100755 (executable)
@@ -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");
+                       }
+               }
+       }
 }