#i119232# remove the oox::xls::CellBlock cache The CellBlock cache introduced in CWS dr78 caused a major performance regression because it resulted in the document being reformatted over and over during XLSX import. As there is no simple fix to avoid the reformattings caused by the CellBlock cache it should be removed altogether. --- a/main/oox/inc/oox/xls/pivotcachebuffer.hxx +++ b/main/oox/inc/oox/xls/pivotcachebuffer.hxx @@ -466,9 +466,7 @@ private: PCDefinitionModel maDefModel; /// Global pivot cache settings. PCSourceModel maSourceModel; /// Pivot cache source settings. PCWorksheetSourceModel maSheetSrcModel; /// Sheet source data if cache type is sheet. - ValueRangeSet maColSpans; /// Column spans used by SheetDataBuffer for optimized cell import. ::rtl::OUString maTargetUrl; /// URL of an external source document. - mutable sal_Int32 mnCurrRow; /// Current row index in dummy sheet. bool mbValidSource; /// True = pivot cache is based on supported data source. bool mbDummySheet; /// True = pivot cache is based on a dummy sheet. }; --- a/main/oox/inc/oox/xls/sheetdatabuffer.hxx +++ b/main/oox/inc/oox/xls/sheetdatabuffer.hxx @@ -86,95 +86,6 @@ struct DataTableModel // ============================================================================ -/** Stores position and contents of a range of cells for optimized import. */ -class CellBlock : public WorksheetHelper -{ -public: - explicit CellBlock( const WorksheetHelper& rHelper, const ValueRange& rColSpan, sal_Int32 nRow ); - - /** Returns true, if the end index of the passed colspan is greater than - the own column end index, or if the passed range has the same end index - but the start indexes do not match. */ - bool isBefore( const ValueRange& rColSpan ) const; - /** Returns true, if the cell block can be expanded with the passed colspan. */ - bool isExpandable( const ValueRange& rColSpan ) const; - /** Returns true, if the own colspan contains the passed column. */ - bool contains( sal_Int32 nCol ) const; - - /** Returns the specified cell from the last row in the cell buffer array. */ - ::com::sun::star::uno::Any& getCellAny( sal_Int32 nCol ); - /** Inserts a rich-string into the cell block. */ - void insertRichString( - const ::com::sun::star::table::CellAddress& rAddress, - const RichStringRef& rxString, - const Font* pFirstPortionFont ); - - /** Appends a new row to the cell buffer array. */ - void startNextRow(); - /** Writes all buffered cells into the Calc sheet. */ - void finalizeImport(); - -private: - /** Fills unused cells before passed index with empty strings. */ - void fillUnusedCells( sal_Int32 nIndex ); - -private: - /** Stores position and string data of a rich-string cell. */ - struct RichStringCell - { - ::com::sun::star::table::CellAddress - maCellAddr; /// The address of the rich-string cell. - RichStringRef mxString; /// The string with rich formatting. - const Font* mpFirstPortionFont; /// Font information from cell for first text portion. - - explicit RichStringCell( - const ::com::sun::star::table::CellAddress& rCellAddr, - const RichStringRef& rxString, - const Font* pFirstPortionFont ); - }; - typedef ::std::list< RichStringCell > RichStringCellList; - - ::com::sun::star::table::CellRangeAddress - maRange; /// Cell range covered by this cell block. - RichStringCellList maRichStrings; /// Cached rich-string cells. - ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > > - maCellArray; /// The array of cells of this cell block. - ::com::sun::star::uno::Any* - mpCurrCellRow; /// Pointer to first cell of current row (last row in maCellArray). - const sal_Int32 mnRowLength; /// Number of cells covered by row of this cell block. - sal_Int32 mnFirstFreeIndex; /// Relative index of first unused cell in current row. -}; - -// ============================================================================ - -/** Manages all cell blocks currently in use. */ -class CellBlockBuffer : public WorksheetHelper -{ -public: - explicit CellBlockBuffer( const WorksheetHelper& rHelper ); - - /** Sets column span information for a row. */ - void setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ); - - /** Tries to find a cell block. Recalculates the map of cell blocks, if the - passed cell address is located in another row than the last cell. */ - CellBlock* getCellBlock( const ::com::sun::star::table::CellAddress& rCellAddr ); - - /** Inserts all cells of all open cell blocks into the Calc document. */ - void finalizeImport(); - -private: - typedef ::std::map< sal_Int32, ValueRangeVector > ColSpanVectorMap; - typedef RefMap< sal_Int32, CellBlock > CellBlockMap; - - ColSpanVectorMap maColSpans; /// Buffereed column spans, mapped by row index. - CellBlockMap maCellBlocks; /// All open cell blocks, mapped by last (!) column of the block span. - CellBlockMap::iterator maCellBlockIt; /// Pointer to cell block currently in use. - sal_Int32 mnCurrRow; /// Current row index used for buffered cell import. -}; - -// ============================================================================ - /** Manages the cell contents and cell formatting of a sheet. */ class SheetDataBuffer : public WorksheetHelper @@ -328,7 +239,6 @@ private: }; typedef ::std::list< MergedRange > MergedRangeList; - CellBlockBuffer maCellBlocks; /// Manages all open cell blocks. ArrayFormulaList maArrayFormulas; /// All array formulas in the sheet. TableOperationList maTableOperations; /// All table operations in the sheet. SharedFormulaMap maSharedFormulas; /// Maps shared formula base address to defined name token index. --- a/main/oox/source/xls/pivotcachebuffer.cxx +++ b/main/oox/source/xls/pivotcachebuffer.cxx @@ -1042,7 +1042,6 @@ PCWorksheetSourceModel::PCWorksheetSourceModel() PivotCache::PivotCache( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper ), - mnCurrRow( -1 ), mbValidSource( false ), mbDummySheet( false ) { @@ -1275,8 +1274,6 @@ void PivotCache::writeSourceHeaderCells( WorksheetHelper& rSheetHelper ) const sal_Int32 nCol = maSheetSrcModel.maRange.StartColumn; sal_Int32 nMaxCol = getAddressConverter().getMaxApiAddress().Column; sal_Int32 nRow = maSheetSrcModel.maRange.StartRow; - mnCurrRow = -1; - updateSourceDataRow( rSheetHelper, nRow ); for( PivotCacheFieldVector::const_iterator aIt = maDatabaseFields.begin(), aEnd = maDatabaseFields.end(); (aIt != aEnd) && (nCol <= nMaxCol); ++aIt, ++nCol ) (*aIt)->writeSourceHeaderCell( rSheetHelper, nCol, nRow ); } @@ -1287,7 +1284,6 @@ void PivotCache::writeSourceDataCell( WorksheetHelper& rSheetHelper, sal_Int32 n OSL_ENSURE( (maSheetSrcModel.maRange.StartColumn <= nCol) && (nCol <= maSheetSrcModel.maRange.EndColumn), "PivotCache::writeSourceDataCell - invalid column index" ); sal_Int32 nRow = maSheetSrcModel.maRange.StartRow + nRowIdx; OSL_ENSURE( (maSheetSrcModel.maRange.StartRow < nRow) && (nRow <= maSheetSrcModel.maRange.EndRow), "PivotCache::writeSourceDataCell - invalid row index" ); - updateSourceDataRow( rSheetHelper, nRow ); if( const PivotCacheField* pCacheField = maDatabaseFields.get( nColIdx ).get() ) pCacheField->writeSourceDataCell( rSheetHelper, nCol, nRow, rItem ); } @@ -1430,22 +1426,12 @@ void PivotCache::prepareSourceDataSheet() // check range location, do not allow ranges that overflow the sheet partly if( getAddressConverter().checkCellRange( rRange, false, true ) ) { - maColSpans.insert( ValueRange( rRange.StartColumn, rRange.EndColumn ) ); OUString aSheetName = CREATE_OUSTRING( "DPCache_" ) + maSheetSrcModel.maSheet; rRange.Sheet = getWorksheets().insertEmptySheet( aSheetName, false ); mbValidSource = mbDummySheet = rRange.Sheet >= 0; } } -void PivotCache::updateSourceDataRow( WorksheetHelper& rSheetHelper, sal_Int32 nRow ) const -{ - if( mnCurrRow != nRow ) - { - rSheetHelper.getSheetData().setColSpans( nRow, maColSpans ); - mnCurrRow = nRow; - } -} - // ============================================================================ PivotCacheBuffer::PivotCacheBuffer( const WorkbookHelper& rHelper ) : --- a/main/oox/source/xls/sheetdatabuffer.cxx +++ b/main/oox/source/xls/sheetdatabuffer.cxx @@ -107,223 +107,20 @@ DataTableModel::DataTableModel() : // ============================================================================ -namespace { - -const sal_Int32 CELLBLOCK_MAXROWS = 16; /// Number of rows in a cell block. - -} // namespace - -CellBlock::CellBlock( const WorksheetHelper& rHelper, const ValueRange& rColSpan, sal_Int32 nRow ) : - WorksheetHelper( rHelper ), - maRange( rHelper.getSheetIndex(), rColSpan.mnFirst, nRow, rColSpan.mnLast, nRow ), - mnRowLength( rColSpan.mnLast - rColSpan.mnFirst + 1 ), - mnFirstFreeIndex( 0 ) -{ - maCellArray.realloc( 1 ); - maCellArray[ 0 ].realloc( mnRowLength ); - mpCurrCellRow = maCellArray[ 0 ].getArray(); -} - -bool CellBlock::isExpandable( const ValueRange& rColSpan ) const -{ - return (maRange.StartColumn == rColSpan.mnFirst) && (maRange.EndColumn == rColSpan.mnLast); -} - -bool CellBlock::isBefore( const ValueRange& rColSpan ) const -{ - return (maRange.EndColumn < rColSpan.mnLast) || - ((maRange.EndColumn == rColSpan.mnLast) && (maRange.StartColumn != rColSpan.mnFirst)); -} - -bool CellBlock::contains( sal_Int32 nCol ) const -{ - return (maRange.StartColumn <= nCol) && (nCol <= maRange.EndColumn); -} - -void CellBlock::insertRichString( const CellAddress& rAddress, const RichStringRef& rxString, const Font* pFirstPortionFont ) -{ - maRichStrings.push_back( RichStringCell( rAddress, rxString, pFirstPortionFont ) ); -} - -void CellBlock::startNextRow() -{ - // fill last cells in current row with empty strings (placeholder for empty cells) - fillUnusedCells( mnRowLength ); - // flush if the cell block reaches maximum size - if( maCellArray.getLength() == CELLBLOCK_MAXROWS ) - { - finalizeImport(); - maRange.StartRow = ++maRange.EndRow; - maCellArray.realloc( 1 ); - mpCurrCellRow = maCellArray[ 0 ].getArray(); - } - else - { - // prepare next row - ++maRange.EndRow; - sal_Int32 nRowCount = maCellArray.getLength(); - maCellArray.realloc( nRowCount + 1 ); - maCellArray[ nRowCount ].realloc( mnRowLength ); - mpCurrCellRow = maCellArray[ nRowCount ].getArray(); - } - mnFirstFreeIndex = 0; -} - -Any& CellBlock::getCellAny( sal_Int32 nCol ) -{ - OSL_ENSURE( contains( nCol ), "CellBlock::getCellAny - invalid column" ); - // fill cells before passed column with empty strings (the placeholder for empty cells) - sal_Int32 nIndex = nCol - maRange.StartColumn; - fillUnusedCells( nIndex ); - mnFirstFreeIndex = nIndex + 1; - return mpCurrCellRow[ nIndex ]; -} - -void CellBlock::finalizeImport() -{ - // fill last cells in last row with empty strings (placeholder for empty cells) - fillUnusedCells( mnRowLength ); - // insert all buffered cells into the Calc sheet - try - { - Reference< XCellRangeData > xRangeData( getCellRange( maRange ), UNO_QUERY_THROW ); - xRangeData->setDataArray( maCellArray ); - } - catch( Exception& ) - { - } - // insert uncacheable cells separately - for( RichStringCellList::const_iterator aIt = maRichStrings.begin(), aEnd = maRichStrings.end(); aIt != aEnd; ++aIt ) - putRichString( aIt->maCellAddr, *aIt->mxString, aIt->mpFirstPortionFont ); -} - -// private -------------------------------------------------------------------- - -CellBlock::RichStringCell::RichStringCell( const CellAddress& rCellAddr, const RichStringRef& rxString, const Font* pFirstPortionFont ) : - maCellAddr( rCellAddr ), - mxString( rxString ), - mpFirstPortionFont( pFirstPortionFont ) -{ -} - -void CellBlock::fillUnusedCells( sal_Int32 nIndex ) -{ - if( mnFirstFreeIndex < nIndex ) - { - Any* pCellEnd = mpCurrCellRow + nIndex; - for( Any* pCell = mpCurrCellRow + mnFirstFreeIndex; pCell < pCellEnd; ++pCell ) - *pCell <<= OUString(); - } -} - -// ============================================================================ - -CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) : - WorksheetHelper( rHelper ), - mnCurrRow( -1 ) -{ - maCellBlockIt = maCellBlocks.end(); -} - -void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ) -{ - OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" ); - OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" ); - if( (mnCurrRow < nRow) && (maColSpans.count( nRow ) == 0) ) - maColSpans[ nRow ] = rColSpans.getRanges(); -} - -CellBlock* CellBlockBuffer::getCellBlock( const CellAddress& rCellAddr ) -{ - OSL_ENSURE( rCellAddr.Row >= mnCurrRow, "CellBlockBuffer::getCellBlock - passed row out of order" ); - // prepare cell blocks, if row changes - if( rCellAddr.Row != mnCurrRow ) - { - // find colspans for the new row - ColSpanVectorMap::iterator aIt = maColSpans.find( rCellAddr.Row ); - - /* Gap between rows, or rows out of order, or no colspan - information for the new row found: flush all open cell blocks. */ - if( (aIt == maColSpans.end()) || (rCellAddr.Row != mnCurrRow + 1) ) - { - finalizeImport(); - maCellBlocks.clear(); - maCellBlockIt = maCellBlocks.end(); - } - - /* Prepare matching cell blocks, create new cell blocks, finalize - unmatching cell blocks, if colspan information is available. */ - if( aIt != maColSpans.end() ) - { - /* The colspan vector aIt points to is sorted by columns, as well - as the cell block map. In the folloing, this vector and the - list of cell blocks can be iterated simultanously. */ - CellBlockMap::iterator aMIt = maCellBlocks.begin(); - const ValueRangeVector& rColRanges = aIt->second; - for( ValueRangeVector::const_iterator aVIt = rColRanges.begin(), aVEnd = rColRanges.end(); aVIt != aVEnd; ++aVIt, ++aMIt ) - { - const ValueRange& rColSpan = *aVIt; - /* Finalize and remove all cell blocks up to end of the column - range (cell blocks are keyed by end column index). - CellBlock::isBefore() returns true, if the end index of the - passed colspan is greater than the column end index of the - cell block, or if the passed range has the same end index - but the start indexes do not match. */ - while( (aMIt != maCellBlocks.end()) && aMIt->second->isBefore( rColSpan ) ) - { - aMIt->second->finalizeImport(); - maCellBlocks.erase( aMIt++ ); - } - /* If the current cell block (aMIt) fits to the colspan, start - a new row there, otherwise create and insert a new cell block. */ - if( (aMIt != maCellBlocks.end()) && aMIt->second->isExpandable( rColSpan ) ) - aMIt->second->startNextRow(); - else - aMIt = maCellBlocks.insert( aMIt, CellBlockMap::value_type( rColSpan.mnLast, - CellBlockMap::mapped_type( new CellBlock( *this, rColSpan, rCellAddr.Row ) ) ) ); - } - // finalize and remove all remaining cell blocks - CellBlockMap::iterator aMEnd = maCellBlocks.end(); - for( CellBlockMap::iterator aMIt2 = aMIt; aMIt2 != aMEnd; ++aMIt2 ) - aMIt2->second->finalizeImport(); - maCellBlocks.erase( aMIt, aMEnd ); - - // remove cached colspan information (including current one aIt points to) - maColSpans.erase( maColSpans.begin(), ++aIt ); - } - maCellBlockIt = maCellBlocks.begin(); - mnCurrRow = rCellAddr.Row; - } - - // try to find a valid cell block (update maCellBlockIt) - if( ((maCellBlockIt != maCellBlocks.end()) && maCellBlockIt->second->contains( rCellAddr.Column )) || - (((maCellBlockIt = maCellBlocks.lower_bound( rCellAddr.Column )) != maCellBlocks.end()) && maCellBlockIt->second->contains( rCellAddr.Column )) ) - { - // maCellBlockIt points to valid cell block - return maCellBlockIt->second.get(); - } - - // no valid cell block found - return 0; -} - -void CellBlockBuffer::finalizeImport() -{ - maCellBlocks.forEachMem( &CellBlock::finalizeImport ); -} - -// ============================================================================ - SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) : WorksheetHelper( rHelper ), - maCellBlocks( rHelper ), mbPendingSharedFmla( false ) { } void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans ) { +#if 0 // the CellBlock cache is no longer used maCellBlocks.setColSpans( nRow, rColSpans ); +#else + (void)nRow; + (void)rColSpans; +#endif } void SheetDataBuffer::setBlankCell( const CellModel& rModel ) @@ -333,19 +130,13 @@ void SheetDataBuffer::setBlankCell( const CellModel& rModel ) void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue ) { - if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) ) - pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= fValue; - else - putValue( rModel.maCellAddr, fValue ); + putValue( rModel.maCellAddr, fValue ); setCellFormat( rModel ); } void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText ) { - if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) ) - pCellBlock->getCellAny( rModel.maCellAddr.Column ) <<= rText; - else - putString( rModel.maCellAddr, rText ); + putString( rModel.maCellAddr, rText ); setCellFormat( rModel ); } @@ -360,10 +151,7 @@ void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRe } else { - if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rModel.maCellAddr ) ) - pCellBlock->insertRichString( rModel.maCellAddr, rxString, pFirstPortionFont ); - else - putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont ); + putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont ); setCellFormat( rModel ); } } @@ -528,9 +316,6 @@ void SheetDataBuffer::setStandardNumFmt( const CellAddress& rCellAddr, sal_Int16 void SheetDataBuffer::finalizeImport() { - // insert all cells of all open cell blocks - maCellBlocks.finalizeImport(); - // create all array formulas for( ArrayFormulaList::iterator aIt = maArrayFormulas.begin(), aEnd = maArrayFormulas.end(); aIt != aEnd; ++aIt ) finalizeArrayFormula( aIt->first, aIt->second ); @@ -658,10 +443,7 @@ void SheetDataBuffer::setCellFormula( const CellAddress& rCellAddr, const ApiTok { if( rTokens.hasElements() ) { - if( CellBlock* pCellBlock = maCellBlocks.getCellBlock( rCellAddr ) ) - pCellBlock->getCellAny( rCellAddr.Column ) <<= rTokens; - else - putFormulaTokens( rCellAddr, rTokens ); + putFormulaTokens( rCellAddr, rTokens ); } }