diff options
-rw-r--r-- | sc/inc/externalrefmgr.hxx | 27 | ||||
-rw-r--r-- | sc/source/filter/xml/xmltabi.cxx | 3 | ||||
-rw-r--r-- | sc/source/ui/docshell/externalrefmgr.cxx | 191 |
3 files changed, 191 insertions, 30 deletions
diff --git a/sc/inc/externalrefmgr.hxx b/sc/inc/externalrefmgr.hxx index ff00b47d08b9..b9d1394a460a 100644 --- a/sc/inc/externalrefmgr.hxx +++ b/sc/inc/externalrefmgr.hxx @@ -249,7 +249,7 @@ public: const TokenArrayRef& pArray); bool isDocInitialized(sal_uInt16 nFileId); - void initializeDoc(sal_uInt16 nFileId, const ::std::vector<OUString>& rTabNames); + void initializeDoc(sal_uInt16 nFileId, const ::std::vector<OUString>& rTabNames, const OUString& rBaseName); OUString getTableName(sal_uInt16 nFileId, size_t nCacheId) const; void getAllTableNames(sal_uInt16 nFileId, ::std::vector<OUString>& rTabNames) const; SCsTAB getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const; @@ -277,6 +277,8 @@ public: */ void getAllCachedDataSpans( sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const; + bool getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab, sal_uInt16 nFileId ) const; + private: struct ReferencedStatus { @@ -302,7 +304,8 @@ private: public: ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const; - ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex); + ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, + size_t* pnIndex, const OUString* pExtUrl); /** * Clear all caches including the cache tables. @@ -346,9 +349,18 @@ private: /** Upper- to real-case mapping for range names. */ NamePairMap maRealRangeNameMap; + /** Either the base name that was stored as sheet name for CSV files if + sheet name is Sheet1, or Sheet1 name if sheet name is base name. + */ + OUString maSingleTableNameAlias; + bool mbInitFromSource; DocItem() : mbInitFromSource(false) {} + + TableNameIndexMap::const_iterator findTableNameIndex( const OUString& rTabName ) const; + bool getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const; + bool getSingleTableNameAlternative( OUString& rTabName ) const; }; typedef std::unordered_map<sal_uInt16, DocItem> DocDataType; DocItem* getDocItem(sal_uInt16 nFileId) const; @@ -460,7 +472,8 @@ public: * table orders are critical.</I> * * Excel filter calls this method to populate the cache table from the - * XCT/CRN records. + * XCT/CRN records. ODF import calls it for cached tables for external + * references. * * @param nFileId file ID * @param rTabName table name @@ -469,10 +482,14 @@ public: * specified table's cache doesn't exist. * @param pnIndex if non-NULL pointer is passed, it stores the internal * index of a cache table instance. + * @param pExtUrl if non-NULL and bCreateNew==true, the base name will be + * propagated as an alias for the first table (and removed + * later if further tables are created). * * @return shared_ptr to the cache table instance */ - ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex = nullptr); + ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, + size_t* pnIndex = nullptr, const OUString* pExtUrl = nullptr); /** Returns a vector containing all (real) table names and cache tables of the specified file. @@ -714,6 +731,8 @@ private: void fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const; + bool getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab, sal_uInt16 nFileId ) const; + ScExternalRefCache::TokenRef getSingleRefTokenFromSrcDoc( sal_uInt16 nFileId, ScDocument* pSrcDoc, const ScAddress& rPos, ScExternalRefCache::CellFormat* pFmt); diff --git a/sc/source/filter/xml/xmltabi.cxx b/sc/source/filter/xml/xmltabi.cxx index 88fa5ed54b85..25042ba35e4b 100644 --- a/sc/source/filter/xml/xmltabi.cxx +++ b/sc/source/filter/xml/xmltabi.cxx @@ -202,7 +202,8 @@ ScXMLTableContext::ScXMLTableContext( ScXMLImport& rImport, { ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); pExternalRefInfo->mnFileId = pRefMgr->getExternalFileId(aExtUrl); - pExternalRefInfo->mpCacheTable = pRefMgr->getCacheTable(pExternalRefInfo->mnFileId, aExtTabName, true); + pExternalRefInfo->mpCacheTable = pRefMgr->getCacheTable(pExternalRefInfo->mnFileId, aExtTabName, true, + nullptr, &aExtUrl); pExternalRefInfo->mpCacheTable->setWholeTableCached(); } } diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx index 6d31312bc42d..3db616cc6b8f 100644 --- a/sc/source/ui/docshell/externalrefmgr.cxx +++ b/sc/source/ui/docshell/externalrefmgr.cxx @@ -32,6 +32,7 @@ #include "sc.hrc" #include "globstr.hrc" #include "cellvalue.hxx" +#include "defaultsoptions.hxx" #include <osl/file.hxx> #include <sfx2/app.hxx> @@ -496,8 +497,7 @@ const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const O } const DocItem& rDoc = itrDoc->second; - TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( - ScGlobal::pCharClass->uppercase(rTabName)); + TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName); if (itrTabId == rDoc.maTableNameIndex.end()) { // the specified table is not in cache. @@ -541,8 +541,7 @@ ScExternalRefCache::TokenRef ScExternalRefCache::getCellData( } const DocItem& rDoc = itrDoc->second; - TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( - ScGlobal::pCharClass->uppercase(rTabName)); + TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName); if (itrTabId == rDoc.maTableNameIndex.end()) { // the specified table is not in cache. @@ -571,8 +570,7 @@ ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( DocItem& rDoc = itrDoc->second; - TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find( - ScGlobal::pCharClass->uppercase(rTabName)); + TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName); if (itrTabId == rDoc.maTableNameIndex.end()) // the specified table is not in cache. return TokenArrayRef(); @@ -802,8 +800,7 @@ void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabNam DocItem& rDoc = *pDocItem; // See if the table by this name already exists. - TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( - ScGlobal::pCharClass->uppercase(rTabName)); + TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rTabName); if (itrTabName == rDoc.maTableNameIndex.end()) // Table not found. Maybe the table name or the file id is wrong ??? return; @@ -833,8 +830,7 @@ void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRa // Now, find the table position of the first table to cache. const OUString& rFirstTabName = rData.front().maTableName; - TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( - ScGlobal::pCharClass->uppercase(rFirstTabName)); + TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rFirstTabName); if (itrTabName == rDoc.maTableNameIndex.end()) { // table index not found. @@ -906,7 +902,7 @@ bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId) return pDoc->mbInitFromSource; } -static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex) +static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex) { ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName); if (itr == rMap.end()) @@ -916,7 +912,29 @@ static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& r return true; } -void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames) +bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const +{ + ScExternalRefCache::TableNameIndexMap::const_iterator itr = findTableNameIndex(rTabName); + if (itr == maTableNameIndex.end()) + return false; + + rIndex = itr->second; + return true; +} + +namespace { +OUString getFirstSheetName() +{ + // Get Custom prefix. + const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions(); + // Form sheet name identical to the first generated sheet name when + // creating an internal document, e.g. 'Sheet1'. + return rOpt.GetInitTabPrefix() + "1"; +} +} + +void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames, + const OUString& rBaseName) { DocItem* pDoc = getDocItem(nFileId); if (!pDoc) @@ -943,7 +961,7 @@ void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString for (size_t i = 0; i < n; ++i) { size_t nIndex; - if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex)) + if (lcl_getStrictTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex)) { aNewTables[i] = pDoc->maTables[nIndex]; } @@ -956,9 +974,94 @@ void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i)); pDoc->maTableNameIndex.swap(aNewNameIndex); + // Setup name for Sheet1 vs base name to be able to load documents + // that store the base name as table name, or vice versa. + pDoc->maSingleTableNameAlias.clear(); + if (!rBaseName.isEmpty() && pDoc->maTableNames.size() == 1) + { + OUString aSheetName = getFirstSheetName(); + // If the one and only table name matches exactly, carry on the base + // file name for further alias use. If instead the table name matches + // the base name, carry on the sheet name as alias. + if (ScGlobal::GetpTransliteration()->isEqual( pDoc->maTableNames[0].maRealName, aSheetName)) + pDoc->maSingleTableNameAlias = rBaseName; + else if (ScGlobal::GetpTransliteration()->isEqual( pDoc->maTableNames[0].maRealName, rBaseName)) + pDoc->maSingleTableNameAlias = aSheetName; + + /* TODO: future versions could swap the table name with the base name, + * so the base name gets stored instead of Sheet1, which can be + * localized and then will not match in another localized UI once the + * link was updated/refreshed. This never worked before + * a4f09f8c2ef3ae82b86d1b4f0c6c90d1a61614fa accidentally "fixed" it for + * fdo#73552 only for CSV files but broke older existing documents. + * Either that, or always store 'Sheet1' or something? + * However, do that only for imported files that do not store sheet + * names, e.g. CSV, HTML, ..., but not for spreadsheet documents. */ + + } + pDoc->mbInitFromSource = true; } +ScExternalRefCache::TableNameIndexMap::const_iterator ScExternalRefCache::DocItem::findTableNameIndex( + const OUString& rTabName ) const +{ + const OUString aTabNameUpper = ScGlobal::pCharClass->uppercase( rTabName); + TableNameIndexMap::const_iterator itrTabName = maTableNameIndex.find( aTabNameUpper); + if (itrTabName != maTableNameIndex.end()) + return itrTabName; + + // For some time for external references to .csv files the base name was + // used as sheet name instead of Sheet1, check if we can resolve that. + // Also helps users that got accustomed to give the base name instead of + // Sheet1. + if (maSingleTableNameAlias.isEmpty() || maTableNameIndex.size() != 1) + return itrTabName; + + // maSingleTableNameAlias has been set up only if the original file loaded + // had exactly one sheet and internal sheet name was Sheet1 or localized or + // customized equivalent, or base name. + if (aTabNameUpper == ScGlobal::pCharClass->uppercase( maSingleTableNameAlias)) + return maTableNameIndex.begin(); + + return itrTabName; +} + +bool ScExternalRefCache::DocItem::getSingleTableNameAlternative( OUString& rTabName ) const +{ + if (maSingleTableNameAlias.isEmpty() || maTableNames.size() != 1) + return false; + if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maTableNames[0].maRealName)) + { + rTabName = maSingleTableNameAlias; + return true; + } + if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maSingleTableNameAlias)) + { + rTabName = maTableNames[0].maRealName; + return true; + } + return false; +} + +bool ScExternalRefCache::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab, + sal_uInt16 nFileId ) const +{ + bool bFound = rSrcDoc.GetTable( rTabName, rTab); + if (!bFound) + { + // Check the one table alias alternative. + const DocItem* pDoc = getDocItem( nFileId ); + if (pDoc) + { + OUString aTabName( rTabName); + if (pDoc->getSingleTableNameAlternative( aTabName)) + bFound = rSrcDoc.GetTable( aTabName, rTab); + } + } + return bFound; +} + OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const { if( DocItem* pDoc = getDocItem( nFileId ) ) @@ -1056,8 +1159,7 @@ bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUSt if (pDoc) { size_t nIndex = 0; - OUString aTabNameUpper = ScGlobal::pCharClass->uppercase( rTabName); - if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex)) + if (pDoc->getTableDataIndex( rTabName, nIndex)) { size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size()); for (size_t i = nIndex; i < nStop; ++i) @@ -1250,7 +1352,8 @@ ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nF return pDoc->maTables[nTabIndex]; } -ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex) +ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName, + bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl) { // In API, the index is transported as cached sheet ID of type sal_Int32 in // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet @@ -1268,8 +1371,7 @@ ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nF DocItem& rDoc = *pDoc; size_t nIndex; - OUString aTabNameUpper = ScGlobal::pCharClass->uppercase(rTabName); - if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex)) + if (rDoc.getTableDataIndex(rTabName, nIndex)) { // specified table found. if( pnIndex ) *pnIndex = nIndex; @@ -1285,7 +1387,27 @@ ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nF return TableTypeRef(); } + // If this is the first table to be created propagate the base name or + // Sheet1 as an alias. For subsequent tables remove it again. + if (rDoc.maTableNames.empty()) + { + if (pExtUrl) + { + const OUString aBaseName( INetURLObject( *pExtUrl).GetBase()); + const OUString aSheetName( getFirstSheetName()); + if (ScGlobal::GetpTransliteration()->isEqual( rTabName, aSheetName)) + pDoc->maSingleTableNameAlias = aBaseName; + else if (ScGlobal::GetpTransliteration()->isEqual( rTabName, aBaseName)) + pDoc->maSingleTableNameAlias = aSheetName; + } + } + else + { + rDoc.maSingleTableNameAlias.clear(); + } + // Specified table doesn't exist yet. Create one. + OUString aTabNameUpper = ScGlobal::pCharClass->uppercase(rTabName); nIndex = rDoc.maTables.size(); if( pnIndex ) *pnIndex = nIndex; TableTypeRef pTab(new Table); @@ -1588,9 +1710,9 @@ ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 } ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable( - sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex) + sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl) { - return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex); + return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex, pExtUrl); } ScExternalRefManager::LinkListener::LinkListener() @@ -1734,7 +1856,7 @@ void putRangeDataIntoCache( // Make sure to set this range 'cached', to prevent unnecessarily // accessing the src document time and time again. ScExternalRefCache::TableTypeRef pCacheTab = - rRefCache.getCacheTable(nFileId, rTabName, true, nullptr); + rRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr); if (pCacheTab) pCacheTab->setCachedCellRange( rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row()); @@ -1772,12 +1894,31 @@ void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sa pSrcDoc->GetName(i, aName); aTabNames.push_back(aName); } - rRefCache.initializeDoc(nFileId, aTabNames); + + // Obtain the base name, don't bother if there are more than one sheets. + OUString aBaseName; + if (nTabCount == 1) + { + const SfxObjectShell* pShell = pSrcDoc->GetDocumentShell(); + if (pShell && pShell->GetMedium()) + { + OUString aName = pShell->GetMedium()->GetName(); + aBaseName = INetURLObject( aName).GetBase(); + } + } + + rRefCache.initializeDoc(nFileId, aTabNames, aBaseName); } } } +bool ScExternalRefManager::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab, + sal_uInt16 nFileId ) const +{ + return maRefCache.getSrcDocTable( rSrcDoc, rTabName, rTab, nFileId); +} + ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell, const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt) @@ -1798,7 +1939,7 @@ ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( { // source document already loaded in memory. Re-use this instance. SCTAB nTab; - if (!pSrcDoc->GetTable(rTabName, nTab)) + if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId)) { // specified table name doesn't exist in the source document. ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(errNoRef)); @@ -1837,7 +1978,7 @@ ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( } SCTAB nTab; - if (!pSrcDoc->GetTable(rTabName, nTab)) + if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId)) { // specified table name doesn't exist in the source document. pToken.reset(new FormulaErrorToken(errNoRef)); @@ -1856,7 +1997,7 @@ ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( // this data, but add it to the cached range to prevent accessing the // source document time and time again. ScExternalRefCache::TableTypeRef pCacheTab = - maRefCache.getCacheTable(nFileId, rTabName, true, nullptr); + maRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr); if (pCacheTab) pCacheTab->setCachedCell(rCell.Col(), rCell.Row()); |