diff options
-rw-r--r-- | sc/inc/scmatrix.hxx | 6 | ||||
-rw-r--r-- | sc/source/core/tool/scmatrix.cxx | 72 | ||||
-rw-r--r-- | sc/source/filter/oox/workbookhelper.cxx | 2 | ||||
-rw-r--r-- | sc/source/ui/docshell/externalrefmgr.cxx | 340 |
4 files changed, 295 insertions, 125 deletions
diff --git a/sc/inc/scmatrix.hxx b/sc/inc/scmatrix.hxx index 9287f556b41f..d05743710642 100644 --- a/sc/inc/scmatrix.hxx +++ b/sc/inc/scmatrix.hxx @@ -160,6 +160,12 @@ public: #endif } + /** Checks nC or nR for zero and uses GetElementsMax() whether a matrix of + the size of nC*nR could be allocated. A zero size (both nC and nR zero) + matrix is allowed for later resize. + */ + bool static IsSizeAllocatable( SCSIZE nC, SCSIZE nR ); + /// Value or boolean. inline static bool IsValueType( ScMatValType nType ) { diff --git a/sc/source/core/tool/scmatrix.cxx b/sc/source/core/tool/scmatrix.cxx index e2dde4e53bf3..2bbe9938447a 100644 --- a/sc/source/core/tool/scmatrix.cxx +++ b/sc/source/core/tool/scmatrix.cxx @@ -333,14 +333,32 @@ void ScMatrixImpl::Clear() void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR) { - maMat.resize(nR, nC); - maMatFlag.resize(nR, nC); + if (ScMatrix::IsSizeAllocatable( nC, nR)) + { + maMat.resize(nR, nC); + maMatFlag.resize(nR, nC); + } + else + { + // Invalid matrix size, allocate 1x1 matrix with error value. + maMat.resize(1, 1, CreateDoubleError( errStackOverflow)); + maMatFlag.resize(1, 1); + } } void ScMatrixImpl::Resize(SCSIZE nC, SCSIZE nR, double fVal) { - maMat.resize(nR, nC, fVal); - maMatFlag.resize(nR, nC); + if (ScMatrix::IsSizeAllocatable( nC, nR)) + { + maMat.resize(nR, nC, fVal); + maMatFlag.resize(nR, nC); + } + else + { + // Invalid matrix size, allocate 1x1 matrix with error value. + maMat.resize(1, 1, CreateDoubleError( errStackOverflow)); + maMatFlag.resize(1, 1); + } } void ScMatrixImpl::SetErrorInterpreter( ScInterpreter* p) @@ -2225,28 +2243,52 @@ void ScMatrix::DecRef() const delete this; } -ScFullMatrix::ScFullMatrix( SCSIZE nC, SCSIZE nR) : - ScMatrix(), - pImpl(new ScMatrixImpl(nC, nR)) +bool ScMatrix::IsSizeAllocatable( SCSIZE nC, SCSIZE nR ) { SAL_WARN_IF( !nC, "sc", "ScMatrix with 0 columns!"); SAL_WARN_IF( !nR, "sc", "ScMatrix with 0 rows!"); + // 0-size matrix is valid, it could be resized later. + if ((nC && !nR) || (!nC && nR)) + { + SAL_WARN( "sc", "ScMatrix one-dimensional zero: " << nC << " columns * " << nR << " rows"); + return false; + } + if (nC && nR && (nC > (ScMatrix::GetElementsMax() / nR))) + { + SAL_WARN( "sc", "ScMatrix overflow: " << nC << " columns * " << nR << " rows"); + return false; + } + return true; +} + +ScFullMatrix::ScFullMatrix( SCSIZE nC, SCSIZE nR) : + ScMatrix() +{ + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( errStackOverflow))); } ScFullMatrix::ScFullMatrix(SCSIZE nC, SCSIZE nR, double fInitVal) : - ScMatrix(), - pImpl(new ScMatrixImpl(nC, nR, fInitVal)) + ScMatrix() { - SAL_WARN_IF( !nC, "sc", "ScMatrix with 0 columns!"); - SAL_WARN_IF( !nR, "sc", "ScMatrix with 0 rows!"); + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR, fInitVal)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( errStackOverflow))); } ScFullMatrix::ScFullMatrix( size_t nC, size_t nR, const std::vector<double>& rInitVals ) : - ScMatrix(), - pImpl(new ScMatrixImpl(nC, nR, rInitVals)) + ScMatrix() { - SAL_WARN_IF( !nC, "sc", "ScMatrix with 0 columns!"); - SAL_WARN_IF( !nR, "sc", "ScMatrix with 0 rows!"); + if (ScMatrix::IsSizeAllocatable( nC, nR)) + pImpl.reset( new ScMatrixImpl( nC, nR, rInitVals)); + else + // Invalid matrix size, allocate 1x1 matrix with error value. + pImpl.reset( new ScMatrixImpl( 1,1, CreateDoubleError( errStackOverflow))); } ScFullMatrix::~ScFullMatrix() diff --git a/sc/source/filter/oox/workbookhelper.cxx b/sc/source/filter/oox/workbookhelper.cxx index 27bee11b41c2..99b889af7274 100644 --- a/sc/source/filter/oox/workbookhelper.cxx +++ b/sc/source/filter/oox/workbookhelper.cxx @@ -604,7 +604,7 @@ void WorkbookGlobals::initialize( bool bWorkbookFile ) // #i76026# disable Undo while loading the document mpDoc->EnableUndo(false); // #i79826# disable calculating automatic row height while loading the document - mpDoc->EnableAdjustHeight(true); + mpDoc->EnableAdjustHeight(false); // disable automatic update of linked sheets and DDE links mpDoc->EnableExecuteLink(false); diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx index 90b570327876..65065ab74cb8 100644 --- a/sc/source/ui/docshell/externalrefmgr.cxx +++ b/sc/source/ui/docshell/externalrefmgr.cxx @@ -580,9 +580,9 @@ ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( const ScAddress& s = rRange.aStart; const ScAddress& e = rRange.aEnd; - SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); - SCCOL nCol1 = s.Col(), nCol2 = e.Col(); - SCROW nRow1 = s.Row(), nRow2 = e.Row(); + const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + const SCCOL nCol1 = s.Col(), nCol2 = e.Col(); + const SCROW nRow1 = s.Row(), nRow2 = e.Row(); // Make sure I have all the tables cached. size_t nTabFirstId = itrTabId->second; @@ -616,58 +616,120 @@ ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( return TokenArrayRef(); } - ScMatrixRef xMat = new ScFullMatrix( - static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); + SCSIZE nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1); + SCSIZE nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + ScMatrixRef xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); - // Only fill non-empty cells, for better performance. + // Needed in shrink and fill. vector<SCROW> aRows; pTab->getAllRows(aRows, nDataRow1, nDataRow2); - for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + bool bFill = true; + + // Check if size could be allocated and if not skip the fill, there's + // one error element instead. But retry first with the actual data area + // if that is smaller than the original range, which works for most + // functions just not some that operate/compare with the original size + // and expect empty values in non-data areas. + // Restrict this though to ranges of entire columns or rows, other + // ranges might be on purpose. (Other special cases to handle?) + /* TODO: sparse matrix could help */ + SCSIZE nMatCols, nMatRows; + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows) { - SCROW nRow = *itr; - vector<SCCOL> aCols; - pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); - for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) + bFill = false; + if (aRows.empty()) { - SCCOL nCol = *itrCol; - TokenRef pToken = pTab->getCell(nCol, nRow); - if (!pToken) - // This should never happen! - return TokenArrayRef(); - - SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; - switch (pToken->GetType()) + // There's no data at all. Set the one matrix element to empty + // for column-repeated and row-repeated access. + xMat->PutEmpty(0,0); + } + else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW)) + { + nDataRow1 = aRows.front(); + nDataRow2 = aRows.back(); + SCCOL nMinCol = std::numeric_limits<SCCOL>::max(); + SCCOL nMaxCol = std::numeric_limits<SCCOL>::min(); + for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + { + vector<SCCOL> aCols; + pTab->getAllCols(*itr, aCols, nDataCol1, nDataCol2); + if (!aCols.empty()) + { + nMinCol = std::min( nMinCol, aCols.front()); + nMaxCol = std::max( nMaxCol, aCols.back()); + } + } + + if (nMinCol <= nMaxCol && ((static_cast<SCSIZE>(nMaxCol-nMinCol+1) < nMatrixColumns) || + (static_cast<SCSIZE>(nDataRow2-nDataRow1+1) < nMatrixRows))) { - case svDouble: - xMat->PutDouble(pToken->GetDouble(), nC, nR); - break; - case svString: - xMat->PutString(pToken->GetString(), nC, nR); - break; - default: - ; + nMatrixColumns = static_cast<SCSIZE>(nMaxCol-nMinCol+1); + nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows) + { + nDataCol1 = nMinCol; + nDataCol2 = nMaxCol; + bFill = true; + } } } } - if (!bFirstTab) - pArray->AddOpCode(ocSep); + if (bFill) + { + // Only fill non-empty cells, for better performance. + for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) + { + SCROW nRow = *itr; + vector<SCCOL> aCols; + pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); + for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) + { + SCCOL nCol = *itrCol; + TokenRef pToken = pTab->getCell(nCol, nRow); + if (!pToken) + // This should never happen! + return TokenArrayRef(); + + SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; + switch (pToken->GetType()) + { + case svDouble: + xMat->PutDouble(pToken->GetDouble(), nC, nR); + break; + case svString: + xMat->PutString(pToken->GetString(), nC, nR); + break; + default: + ; + } + } + } - ScMatrixToken aToken(xMat); - if (!pArray) - pArray.reset(new ScTokenArray); - pArray->AddToken(aToken); + if (!bFirstTab) + pArray->AddOpCode(ocSep); - bFirstTab = false; + ScMatrixToken aToken(xMat); + if (!pArray) + pArray.reset(new ScTokenArray); + pArray->AddToken(aToken); - if (!pNewRange) - pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); - else - pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); + bFirstTab = false; + + if (!pNewRange) + pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab)); + else + pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab)); + } } - if (pNewRange) + rDoc.maRangeArrays.insert( RangeArrayMap::value_type(aCacheRange, pArray)); + if (pNewRange && *pNewRange != aCacheRange) rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray)); + return pArray; } @@ -791,37 +853,56 @@ void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRa pTabData.reset(new Table); const ScMatrixRef& pMat = itrData->mpRangeData; - for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + SCSIZE nMatCols, nMatRows; + pMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols > static_cast<SCSIZE>(nCol2 - nCol1) && nMatRows > static_cast<SCSIZE>(nRow2 - nRow1)) { - const SCSIZE nR = nRow - nRow1; - for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) { - const SCSIZE nC = nCol - nCol1; + const SCSIZE nR = nRow - nRow1; + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + const SCSIZE nC = nCol - nCol1; - ScMatrixValue value = pMat->Get(nC, nR); + ScMatrixValue value = pMat->Get(nC, nR); - TokenRef pToken; + TokenRef pToken; - switch (value.nType) { - case SC_MATVAL_VALUE: - case SC_MATVAL_BOOLEAN: - pToken.reset(new formula::FormulaDoubleToken(value.fVal)); - break; - case SC_MATVAL_STRING: - pToken.reset(new formula::FormulaStringToken(value.aStr)); - break; - default: - // Don't cache empty cells. - break; - } + switch (value.nType) { + case SC_MATVAL_VALUE: + case SC_MATVAL_BOOLEAN: + pToken.reset(new formula::FormulaDoubleToken(value.fVal)); + break; + case SC_MATVAL_STRING: + pToken.reset(new formula::FormulaStringToken(value.aStr)); + break; + default: + // Don't cache empty cells. + break; + } - if (pToken) - // Don't mark this cell 'cached' here, for better performance. - pTabData->setCell(nCol, nRow, pToken, 0, false); + if (pToken) + // Don't mark this cell 'cached' here, for better performance. + pTabData->setCell(nCol, nRow, pToken, 0, false); + } + } + // Mark the whole range 'cached'. + pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); + } + else + { + // This may happen due to a matrix not been allocated earlier, in + // which case it should have exactly one error element. + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch"); + if (nMatCols != 1 || nMatRows != 1) + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix"); + else + { + sal_uInt16 nErr = GetDoubleErrorValue( pMat->GetDouble(0,0)); + SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << nErr << + (nErr ? ", ok" : ", not ok")); } } - // Mark the whole range 'cached'. - pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); } size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab(); @@ -1467,9 +1548,9 @@ static std::unique_ptr<ScTokenArray> convertToTokenArray( ScAddress& s = rRange.aStart; ScAddress& e = rRange.aEnd; - SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); - SCCOL nCol1 = s.Col(), nCol2 = e.Col(); - SCROW nRow1 = s.Row(), nRow2 = e.Row(); + const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); + const SCCOL nCol1 = s.Col(), nCol2 = e.Col(); + const SCROW nRow1 = s.Row(), nRow2 = e.Row(); if (nTab2 != nTab1) // For now, we don't support multi-sheet ranges intentionally because @@ -1502,62 +1583,103 @@ static std::unique_ptr<ScTokenArray> convertToTokenArray( else pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); - ScMatrixRef xMat = new ScFullMatrix( - static_cast<SCSIZE>(nCol2-nCol1+1), static_cast<SCSIZE>(nRow2-nRow1+1)); - - ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT); - ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE); + SCSIZE nMatrixColumns = static_cast<SCSIZE>(nCol2-nCol1+1); + SCSIZE nMatrixRows = static_cast<SCSIZE>(nRow2-nRow1+1); + ScMatrixRef xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + + bool bFill = true; + SCCOL nEffectiveCol1 = nCol1; + SCROW nEffectiveRow1 = nRow1; + + // Check if size could be allocated and if not skip the fill, there's + // one error element instead. But retry first with the actual data area + // if that is smaller than the original range, which works for most + // functions just not some that operate/compare with the original size + // and expect empty values in non-data areas. + // Restrict this though to ranges of entire columns or rows, other + // ranges might be on purpose. (Other special cases to handle?) + /* TODO: sparse matrix could help */ + SCSIZE nMatCols, nMatRows; + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows) + { + bFill = false; + if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW)) + { + if ((static_cast<SCSIZE>(nDataCol2-nDataCol1+1) < nMatrixColumns) || + (static_cast<SCSIZE>(nDataRow2-nDataRow1+1) < nMatrixRows)) + { + nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1); + nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1); + xMat = new ScFullMatrix( nMatrixColumns, nMatrixRows); + xMat->GetDimensions( nMatCols, nMatRows); + if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows) + { + nEffectiveCol1 = nDataCol1; + nEffectiveRow1 = nDataRow1; + bFill = true; + } + } + } + } - for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) + if (bFill) { - const SCSIZE nC = nCol - nCol1; - for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) + ColumnBatch<svl::SharedString> aStringBatch(pHostDoc, pSrcDoc, CELLTYPE_STRING, CELLTYPE_EDIT); + ColumnBatch<double> aDoubleBatch(pHostDoc, pSrcDoc, CELLTYPE_VALUE, CELLTYPE_VALUE); + + for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) { - const SCSIZE nR = nRow - nRow1; + const SCSIZE nC = nCol - nEffectiveCol1; + for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) + { + const SCSIZE nR = nRow - nEffectiveRow1; - ScRefCellValue aCell(*pSrcDoc, ScAddress(nCol, nRow, nTab)); + ScRefCellValue aCell(*pSrcDoc, ScAddress(nCol, nRow, nTab)); - aStringBatch.update(aCell, nC, nR, xMat); - aDoubleBatch.update(aCell, nC, nR, xMat); + aStringBatch.update(aCell, nC, nR, xMat); + aDoubleBatch.update(aCell, nC, nR, xMat); - if (aCell.hasEmptyValue()) - // Skip empty cells. Matrix's default values are empty elements. - continue; + if (aCell.hasEmptyValue()) + // Skip empty cells. Matrix's default values are empty elements. + continue; - switch (aCell.meType) - { - case CELLTYPE_FORMULA: + switch (aCell.meType) { - ScFormulaCell* pFCell = aCell.mpFormula; - sal_uInt16 nError = pFCell->GetErrCode(); - if (nError) - xMat->PutDouble( CreateDoubleError( nError), nC, nR); - else if (pFCell->IsValue()) - { - double fVal = pFCell->GetValue(); - xMat->PutDouble(fVal, nC, nR); - } - else - { - svl::SharedString aStr = pFCell->GetString(); - aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString()); - xMat->PutString(aStr, nC, nR); - } + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = aCell.mpFormula; + sal_uInt16 nError = pFCell->GetErrCode(); + if (nError) + xMat->PutDouble( CreateDoubleError( nError), nC, nR); + else if (pFCell->IsValue()) + { + double fVal = pFCell->GetValue(); + xMat->PutDouble(fVal, nC, nR); + } + else + { + svl::SharedString aStr = pFCell->GetString(); + aStr = pHostDoc->GetSharedStringPool().intern(aStr.getString()); + xMat->PutString(aStr, nC, nR); + } + } + break; + // These are handled in batch: + case CELLTYPE_VALUE: + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + break; + default: + OSL_FAIL("attempted to convert an unknown cell type."); } - break; - // These are handled in batch: - case CELLTYPE_VALUE: - case CELLTYPE_STRING: - case CELLTYPE_EDIT: - break; - default: - OSL_FAIL("attempted to convert an unknown cell type."); } - } - aStringBatch.flush(nC, xMat); - aDoubleBatch.flush(nC, xMat); + aStringBatch.flush(nC, xMat); + aDoubleBatch.flush(nC, xMat); + } } + if (!bFirstTab) pArray->AddOpCode(ocSep); |