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"); } } }
/** * @param rs record stream with all {@link SharedFormulaRecord} {@link ArrayRecord}, {@link * TableRecord} {@link MergeCellsRecord} Records removed * @param svm an initialised {@link SharedValueManager} (from the shared formula, array and table * records of the current sheet). Never <code>null</code>. */ public RowRecordsAggregate(RecordStream rs, SharedValueManager svm) { this(svm); while (rs.hasNext()) { Record rec = rs.getNext(); switch (rec.getSid()) { case RowRecord.sid: insertRow((RowRecord) rec); continue; case DBCellRecord.sid: // end of 'Row Block'. Should only occur after cell records // ignore DBCELL records because POI generates them upon re-serialization continue; } if (rec instanceof UnknownRecord) { // might need to keep track of where exactly these belong addUnknownRecord(rec); while (rs.peekNextSid() == ContinueRecord.sid) { addUnknownRecord(rs.getNext()); } continue; } if (rec instanceof MulBlankRecord) { _valuesAgg.addMultipleBlanks((MulBlankRecord) rec); continue; } if (!(rec instanceof CellValueRecordInterface)) { throw new RuntimeException("Unexpected record type (" + rec.getClass().getName() + ")"); } _valuesAgg.construct((CellValueRecordInterface) rec, rs, svm); } }
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 DimensionsRecord createDimensions() { DimensionsRecord result = new DimensionsRecord(); result.setFirstRow(_firstrow); result.setLastRow(_lastrow); result.setFirstCol((short) _valuesAgg.getFirstCellNum()); result.setLastCol((short) _valuesAgg.getLastCellNum()); return result; }
@SuppressWarnings( "deprecation") // uses deprecated {@link ValueRecordsAggregate#getValueRecords()} public void testInsertCell() { CellValueRecordInterface[] cvrs = valueRecord.getValueRecords(); assertEquals(0, cvrs.length); BlankRecord blankRecord = newBlankRecord(); valueRecord.insertCell(blankRecord); cvrs = valueRecord.getValueRecords(); assertEquals(1, cvrs.length); }
@SuppressWarnings( "deprecation") // uses deprecated {@link ValueRecordsAggregate#getValueRecords()} public void testRemoveCell() { BlankRecord blankRecord1 = newBlankRecord(); valueRecord.insertCell(blankRecord1); BlankRecord blankRecord2 = newBlankRecord(); valueRecord.removeCell(blankRecord2); CellValueRecordInterface[] cvrs = valueRecord.getValueRecords(); assertEquals(0, cvrs.length); // removing an already empty cell just falls through valueRecord.removeCell(blankRecord2); }
/** * Tests various manipulations of blank cells, to make sure that {@link MulBlankRecord}s are use * appropriately */ public void testMultipleBlanks() { BlankRecord brA2 = newBlankRecord(0, 1); BlankRecord brB2 = newBlankRecord(1, 1); BlankRecord brC2 = newBlankRecord(2, 1); BlankRecord brD2 = newBlankRecord(3, 1); BlankRecord brE2 = newBlankRecord(4, 1); BlankRecord brB3 = newBlankRecord(1, 2); BlankRecord brC3 = newBlankRecord(2, 2); valueRecord.insertCell(brA2); valueRecord.insertCell(brB2); valueRecord.insertCell(brD2); confirmMulBlank(3, 1, 1); valueRecord.insertCell(brC3); confirmMulBlank(4, 1, 2); valueRecord.insertCell(brB3); valueRecord.insertCell(brE2); confirmMulBlank(6, 3, 0); valueRecord.insertCell(brC2); confirmMulBlank(7, 2, 0); valueRecord.removeCell(brA2); confirmMulBlank(6, 2, 0); valueRecord.removeCell(brC2); confirmMulBlank(5, 2, 1); valueRecord.removeCell(brC3); confirmMulBlank(4, 1, 2); }
public IndexRecord createIndexRecord(int indexRecordOffset, int sizeOfInitialSheetRecords) { IndexRecord result = new IndexRecord(); result.setFirstRow(_firstrow); result.setLastRowAdd1(_lastrow + 1); // Calculate the size of the records from the end of the BOF // and up to the RowRecordsAggregate... // Add the references to the DBCells in the IndexRecord (one for each block) // Note: The offsets are relative to the Workbook BOF. Assume that this is // 0 for now..... int blockCount = getRowBlockCount(); // Calculate the size of this IndexRecord int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount); int currentOffset = indexRecordOffset + indexRecSize + sizeOfInitialSheetRecords; for (int block = 0; block < blockCount; block++) { // each row-block has a DBCELL record. // The offset of each DBCELL record needs to be updated in the INDEX record // account for row records in this row-block currentOffset += getRowBlockSize(block); // account for cell value records after those currentOffset += _valuesAgg.getRowCellBlockSize( getStartRowNumberForBlock(block), getEndRowNumberForBlock(block)); // currentOffset is now the location of the DBCELL record for this row-block result.addDbcell(currentOffset); // Add space required to write the DBCELL record (whose reference was just added). currentOffset += (8 + (getRowCountForBlock(block) * 2)); } return result; }
private void constructValueRecord(List<Record> 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); } }
@Override public void visitContainedRecords(RecordVisitor rv) { PositionTrackingVisitor stv = new PositionTrackingVisitor(rv, 0); // DBCells are serialized before row records. final int blockCount = getRowBlockCount(); for (int blockIndex = 0; blockIndex < blockCount; blockIndex++) { // Serialize a block of rows. // Hold onto the position of the first row in the block int pos = 0; // Hold onto the size of this block that was serialized final int rowBlockSize = visitRowRecordsForBlock(blockIndex, rv); pos += rowBlockSize; // Serialize a block of cells for those rows final int startRowNumber = getStartRowNumberForBlock(blockIndex); final int endRowNumber = getEndRowNumberForBlock(blockIndex); DBCellRecord.Builder dbcrBuilder = new DBCellRecord.Builder(); // Note: Cell references start from the second row... int cellRefOffset = (rowBlockSize - RowRecord.ENCODED_SIZE); for (int row = startRowNumber; row <= endRowNumber; row++) { if (_valuesAgg.rowHasCells(row)) { stv.setPosition(0); _valuesAgg.visitCellsForRow(row, stv); int rowCellSize = stv.getPosition(); pos += rowCellSize; // Add the offset to the first cell for the row into the // DBCellRecord. dbcrBuilder.addCellOffset(cellRefOffset); cellRefOffset = rowCellSize; } } // Calculate Offset from the start of a DBCellRecord to the first Row rv.visitRecord(dbcrBuilder.build(pos)); } for (int i = 0; i < _unknownRecords.size(); i++) { // Potentially breaking the file here since we don't know exactly where to write these records rv.visitRecord(_unknownRecords.get(i)); } }
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()); }
public void removeRow(RowRecord row) { int rowIndex = row.getRowNumber(); _valuesAgg.removeAllCellsValuesForRow(rowIndex); Integer key = Integer.valueOf(rowIndex); RowRecord rr = _rowRecords.remove(key); if (rr == null) { throw new RuntimeException("Invalid row index (" + key.intValue() + ")"); } if (row != rr) { _rowRecords.put(key, rr); throw new RuntimeException("Attempt to remove row that does not belong to this sheet"); } }
private void confirmMulBlank( int expectedTotalBlankCells, int expectedNumberOfMulBlankRecords, int expectedNumberOfSingleBlankRecords) { // assumed row ranges set-up by caller: final int firstRow = 1; final int lastRow = 2; final class BlankStats { public int countBlankCells; public int countMulBlankRecords; public int countSingleBlankRecords; } final BlankStats bs = new BlankStats(); RecordVisitor rv = new RecordVisitor() { public void visitRecord(Record r) { if (r instanceof MulBlankRecord) { MulBlankRecord mbr = (MulBlankRecord) r; bs.countMulBlankRecords++; bs.countBlankCells += mbr.getNumColumns(); } else if (r instanceof BlankRecord) { bs.countSingleBlankRecords++; bs.countBlankCells++; } } }; for (int rowIx = firstRow; rowIx <= lastRow; rowIx++) { if (valueRecord.rowHasCells(rowIx)) { valueRecord.visitCellsForRow(rowIx, rv); } } assertEquals(expectedTotalBlankCells, bs.countBlankCells); assertEquals(expectedNumberOfMulBlankRecords, bs.countMulBlankRecords); assertEquals(expectedNumberOfSingleBlankRecords, bs.countSingleBlankRecords); }
public void testSerialize() { byte[] expectedArray = HexRead.readFromString( "" + "06 00 16 00 " // Formula + "01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + "01 02 06 00 " // Blank + "02 00 02 00 00 00"); byte[] actualArray = new byte[expectedArray.length]; List<Record> records = testData(); constructValueRecord(records); SerializerVisitor sv = new SerializerVisitor(actualArray); valueRecord.visitCellsForRow(1, sv); valueRecord.visitCellsForRow(2, sv); assertEquals(actualArray.length, sv.getWriteIndex()); assertTrue(Arrays.equals(expectedArray, actualArray)); }
/** * Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed as * part of the value records */ @SuppressWarnings( "deprecation") // uses deprecated {@link ValueRecordsAggregate#getValueRecords()} public void testSharedFormula() { List<Record> records = new ArrayList<Record>(); records.add(new FormulaRecord()); records.add(new SharedFormulaRecord()); records.add(new WindowTwoRecord()); constructValueRecord(records); CellValueRecordInterface[] cvrs = valueRecord.getValueRecords(); // Ensure that the SharedFormulaRecord has been converted assertEquals(1, cvrs.length); CellValueRecordInterface record = cvrs[0]; assertNotNull("Row contains a value", record); assertTrue( "First record is a FormulaRecordsAggregate", (record instanceof FormulaRecordAggregate)); }
public void removeCell(CellValueRecordInterface cvRec) { if (cvRec instanceof FormulaRecordAggregate) { ((FormulaRecordAggregate) cvRec).notifyFormulaChanging(); } _valuesAgg.removeCell(cvRec); }
public void insertCell(CellValueRecordInterface cvRec) { _valuesAgg.insertCell(cvRec); }
public CellValueRecordInterface[] getValueRecords() { return _valuesAgg.getValueRecords(); }
public void updateFormulasAfterRowShift( FormulaShifter formulaShifter, int currentExternSheetIndex) { _valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex); }
/** Returns an iterator for the cell values */ public Iterator<CellValueRecordInterface> getCellValueIterator() { return _valuesAgg.iterator(); }