diff options
Diffstat (limited to 'sc/source/filter/excel/xilink.cxx')
-rw-r--r-- | sc/source/filter/excel/xilink.cxx | 962 |
1 files changed, 962 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx new file mode 100644 index 000000000..e9fea951e --- /dev/null +++ b/sc/source/filter/excel/xilink.cxx @@ -0,0 +1,962 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xilink.hxx> +#include <document.hxx> +#include <scextopt.hxx> +#include <xistream.hxx> +#include <xihelper.hxx> +#include <xiname.hxx> +#include <xltools.hxx> +#include <excform.hxx> +#include <tokenarray.hxx> +#include <externalrefmgr.hxx> +#include <scmatrix.hxx> +#include <svl/sharedstringpool.hxx> +#include <sal/log.hxx> + +#include <vector> +#include <memory> + +// *** Helper classes *** + +// Cached external cells ====================================================== + +namespace { + +/** + * Contains the address and value of an external referenced cell. + * Note that this is non-copyable, so cannot be used in most stl/boost containers. + */ +class XclImpCrn : public XclImpCachedValue +{ +public: + /** Reads a cached value and stores it with its cell address. */ + explicit XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ); + + const XclAddress& GetAddress() const { return maXclPos;} + +private: + XclAddress maXclPos; /// Excel position of the cached cell. +}; + +// Sheet in an external document ============================================== + +/** Contains the name and sheet index of one sheet in an external document. */ +class XclImpSupbookTab +{ +public: + /** Stores the sheet name and marks the sheet index as invalid. + The sheet index is set while creating the Calc sheet with CreateTable(). */ + explicit XclImpSupbookTab( const OUString& rTabName ); + + const OUString& GetTabName() const { return maTabName; } + + /** Reads a CRN record (external referenced cell) at the specified address. */ + void ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos ); + + void LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable, + svl::SharedStringPool& rPool ); + +private: + typedef std::shared_ptr< XclImpCrn > XclImpCrnRef; + + std::vector< XclImpCrnRef > maCrnList; /// List of CRN records (cached cell values). + OUString maTabName; /// Name of the external sheet. +}; + +} + +// External document (SUPBOOK) ================================================ + +/** This class represents an external linked document (record SUPBOOK). + @descr Contains a list of all referenced sheets in the document. */ +class XclImpSupbook : protected XclImpRoot +{ +public: + /** Reads the SUPBOOK record from stream. */ + explicit XclImpSupbook( XclImpStream& rStrm ); + + /** Reads an XCT record (count of following CRNs and current sheet). */ + void ReadXct( XclImpStream& rStrm ); + /** Reads a CRN record (external referenced cell). */ + void ReadCrn( XclImpStream& rStrm ); + /** Reads an EXTERNNAME record. */ + void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ); + + /** Returns the SUPBOOK record type. */ + XclSupbookType GetType() const { return meType; } + + /** Returns the URL of the external document. */ + const OUString& GetXclUrl() const { return maXclUrl; } + + /** Returns the external name specified by an index from the Excel document (one-based). */ + const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const; + /** Tries to decode the URL to OLE or DDE link components. + @descr For DDE links: Decodes to application name and topic. + For OLE object links: Decodes to class name and document URL. + @return true = decoding was successful, returned strings are valid (not empty). */ + bool GetLinkData( OUString& rApplic, OUString& rDoc ) const; + /** Returns the specified macro name (1-based) or an empty string on error. */ + OUString GetMacroName( sal_uInt16 nXclNameIdx ) const; + + OUString GetTabName( sal_uInt16 nXtiTab ) const; + + sal_uInt16 GetTabCount() const; + + void LoadCachedValues(); + + svl::SharedStringPool& GetSharedStringPool(); + +private: + + std::vector< std::unique_ptr<XclImpSupbookTab> > + maSupbTabList; /// All sheet names of the document. + std::vector< std::unique_ptr<XclImpExtName> > + maExtNameList; /// All external names of the document. + OUString maXclUrl; /// URL of the external document (Excel mode). + XclSupbookType meType; /// Type of the supbook record. + sal_uInt16 mnSBTab; /// Current Excel sheet index from SUPBOOK for XCT/CRN records. +}; + +// Import link manager ======================================================== + +namespace { + +/** Contains the SUPBOOK index and sheet indexes of an external link. + @descr It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1), + therefore here occurs a sheet range. */ +struct XclImpXti +{ + sal_uInt16 mnSupbook; /// Index to SUPBOOK record. + sal_uInt16 mnSBTabFirst; /// Index to the first sheet of the range in the SUPBOOK. + sal_uInt16 mnSBTabLast; /// Index to the last sheet of the range in the SUPBOOK. + explicit XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {} +}; + +XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti ) +{ + rXti.mnSupbook = rStrm.ReaduInt16(); + rXti.mnSBTabFirst = rStrm.ReaduInt16(); + rXti.mnSBTabLast = rStrm.ReaduInt16(); + return rStrm; +} + +} + +/** Implementation of the link manager. */ +class XclImpLinkManagerImpl : protected XclImpRoot +{ +public: + explicit XclImpLinkManagerImpl( const XclImpRoot& rRoot ); + + /** Reads the EXTERNSHEET record. */ + void ReadExternsheet( XclImpStream& rStrm ); + /** Reads a SUPBOOK record. */ + void ReadSupbook( XclImpStream& rStrm ); + /** Reads an XCT record and appends it to the current SUPBOOK. */ + void ReadXct( XclImpStream& rStrm ); + /** Reads a CRN record and appends it to the current SUPBOOK. */ + void ReadCrn( XclImpStream& rStrm ); + /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */ + void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ); + + /** Returns true, if the specified XTI entry contains an internal reference. */ + bool IsSelfRef( sal_uInt16 nXtiIndex ) const; + /** Returns the Calc sheet index range of the specified XTI entry. + @return true = XTI data found, returned sheet index range is valid. */ + bool GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, + sal_uInt16 nXtiIndex ) const; + /** Returns the specified external name or 0 on error. */ + const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const; + + /** Returns the absolute file URL of a supporting workbook specified by + the index. */ + const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const; + + OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const; + + /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components. + @descr For DDE links: Decodes to application name and topic. + For OLE object links: Decodes to class name and document URL. + @return true = decoding was successful, returned strings are valid (not empty). */ + bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const; + /** Returns the specified macro name or an empty string on error. */ + OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const; + +private: + /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */ + const XclImpXti* GetXti( sal_uInt16 nXtiIndex ) const; + /** Returns the specified SUPBOOK (external document). */ + const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const; + + void LoadCachedValues(); + +private: + typedef std::vector< XclImpXti > XclImpXtiVector; + + XclImpXtiVector maXtiList; /// List of all XTI structures. + std::vector< std::unique_ptr<XclImpSupbook> > + maSupbookList; /// List of external documents. +}; + +// *** Implementation *** + +// Excel sheet indexes ======================================================== + +// original Excel sheet names ------------------------------------------------- + +void XclImpTabInfo::AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab ) +{ + maTabNames[ rXclTabName ] = nScTab; +} + +void XclImpTabInfo::InsertScTab( SCTAB nScTab ) +{ + for( auto& rEntry : maTabNames ) + if( rEntry.second >= nScTab ) + ++rEntry.second; +} + +SCTAB XclImpTabInfo::GetScTabFromXclName( const OUString& rXclTabName ) const +{ + XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName ); + return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID; +} + +// record creation order - TABID record --------------------------------------- + +void XclImpTabInfo::ReadTabid( XclImpStream& rStrm ) +{ + OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ); + if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 ) + { + rStrm.EnableDecryption(); + std::size_t nReadCount = rStrm.GetRecLeft() / 2; + OSL_ENSURE( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" ); + maTabIdVec.clear(); + maTabIdVec.reserve( nReadCount ); + for( std::size_t nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex ) + // zero index is not allowed in BIFF8, but it seems that it occurs in real life + maTabIdVec.push_back( rStrm.ReaduInt16() ); + } +} + +sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const +{ + sal_uInt16 nReturn = 0; + for( sal_uInt16 nValue : maTabIdVec ) + { + if( nValue == nCreatedId ) + return nReturn; + if( nValue <= nMaxTabId ) + ++nReturn; + } + return 0; +} + +// External names ============================================================= + +XclImpExtName::MOper::MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm) : + mxCached(new ScMatrix(0,0)) +{ + SCSIZE nLastCol = rStrm.ReaduInt8(); + SCSIZE nLastRow = rStrm.ReaduInt16(); + + //assuming worst case scenario of nOp + one byte unistring len + const size_t nMinRecordSize = 2; + const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * (nLastCol+1)); + if (nLastRow >= nMaxRows) + { + SAL_WARN("sc", "Parsing error: " << nMaxRows << + " max possible rows, but " << nLastRow << " index claimed, truncating"); + if (nMaxRows > 0) + nLastRow = nMaxRows-1; + else + return; + } + + mxCached->Resize(nLastCol+1, nLastRow+1); + for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow) + { + for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol) + { + sal_uInt8 nOp; + nOp = rStrm.ReaduInt8(); + switch (nOp) + { + case 0x01: + { + double fVal = rStrm.ReadDouble(); + mxCached->PutDouble(fVal, nCol, nRow); + } + break; + case 0x02: + { + OUString aStr = rStrm.ReadUniString(); + mxCached->PutString(rPool.intern(aStr), nCol, nRow); + } + break; + case 0x04: + { + bool bVal = rStrm.ReaduInt8(); + mxCached->PutBoolean(bVal, nCol, nRow); + rStrm.Ignore(7); + } + break; + case 0x10: + { + sal_uInt8 nErr = rStrm.ReaduInt8(); + // Map the error code from xls to calc. + mxCached->PutError(XclTools::GetScErrorCode(nErr), nCol, nRow); + rStrm.Ignore(7); + } + break; + default: + rStrm.Ignore(8); + } + } + } +} + +const ScMatrix& XclImpExtName::MOper::GetCache() const +{ + return *mxCached; +} + +XclImpExtName::XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv ) + : mnStorageId(0) +{ + sal_uInt16 nFlags(0); + sal_uInt8 nLen(0); + + nFlags = rStrm.ReaduInt16(); + mnStorageId = rStrm.ReaduInt32(); + nLen = rStrm.ReaduInt8(); + maName = rStrm.ReadUniString( nLen ); + if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) ) + { + if( eSubType == XclSupbookType::Addin ) + { + meType = xlExtAddIn; + maName = XclImpRoot::GetScAddInName( maName ); + } + else if ( (eSubType == XclSupbookType::Eurotool) && + maName.equalsIgnoreAsciiCase( "EUROCONVERT" ) ) + meType = xlExtEuroConvert; + else + { + meType = xlExtName; + maName = ScfTools::ConvertToScDefinedName( maName ); + } + } + else + { + meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE ); + } + + switch (meType) + { + case xlExtDDE: + if (rStrm.GetRecLeft() > 1) + mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm)); + break; + case xlExtName: + // TODO: For now, only global external names are supported. In future + // we should extend this to supporting per-sheet external names. + if (mnStorageId == 0 && pFormulaConv) + { + std::unique_ptr<ScTokenArray> pArray; + sal_uInt16 nFmlaLen; + nFmlaLen = rStrm.ReaduInt16(); + std::vector<OUString> aTabNames; + sal_uInt16 nCount = rSupbook.GetTabCount(); + aTabNames.reserve(nCount); + for (sal_uInt16 i = 0; i < nCount; ++i) + aTabNames.push_back(rSupbook.GetTabName(i)); + + pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames); + if (pArray) + mxArray = std::move( pArray ); + } + break; + case xlExtOLE: + mpMOper.reset( new MOper(rSupbook.GetSharedStringPool(), rStrm) ); + break; + default: + ; + } +} + +XclImpExtName::~XclImpExtName() +{ +} + +void XclImpExtName::CreateDdeData( ScDocument& rDoc, const OUString& rApplic, const OUString& rTopic ) const +{ + ScMatrixRef xResults; + if( mxDdeMatrix ) + xResults = mxDdeMatrix->CreateScMatrix(rDoc.GetSharedStringPool()); + rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults ); +} + +void XclImpExtName::CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const +{ + if (!mxArray) + return; + + ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); + pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray); +} + +namespace { + +/** + * Decompose the name into sheet name and range name. An OLE link name is + * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1 + * notation. + */ +bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange) +{ + sal_Int32 n = rName.getLength(); + const sal_Unicode* p = rName.getStr(); + OUStringBuffer aBuf; + bool bInSheet = true; + for (sal_Int32 i = 0; i < n; ++i, ++p) + { + if (i == 0) + { + // first character must be '!'. + if (*p != '!') + return false; + continue; + } + + if (*p == '!') + { + // sheet name to range separator. + if (!bInSheet) + return false; + rSheet = aBuf.makeStringAndClear(); + bInSheet = false; + continue; + } + + aBuf.append(*p); + } + + rRange = aBuf.makeStringAndClear(); + return true; +} + +} + +bool XclImpExtName::CreateOleData(const ScDocument& rDoc, const OUString& rUrl, + sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const +{ + if (!mpMOper) + return false; + + OUString aSheet, aRangeStr; + if (!extractSheetAndRange(maName, aSheet, aRangeStr)) + return false; + + ScRange aRange; + ScRefFlags nRes = aRange.ParseAny(aRangeStr, rDoc, formula::FormulaGrammar::CONV_XL_R1C1); + if ((nRes & ScRefFlags::VALID) == ScRefFlags::ZERO) + return false; + + if (aRange.aStart.Tab() != aRange.aEnd.Tab()) + // We don't support multi-sheet range for this. + return false; + + const ScMatrix& rCache = mpMOper->GetCache(); + SCSIZE nC, nR; + rCache.GetDimensions(nC, nR); + if (!nC || !nR) + // cache matrix is empty. + return false; + + ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager(); + sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl); + ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true); + if (!xTab) + // cache table creation failed. + return false; + + xTab->setWholeTableCached(); + for (SCSIZE i = 0; i < nR; ++i) + { + for (SCSIZE j = 0; j < nC; ++j) + { + SCCOL nCol = aRange.aStart.Col() + j; + SCROW nRow = aRange.aStart.Row() + i; + + ScMatrixValue aVal = rCache.Get(j, i); + switch (aVal.nType) + { + case ScMatValType::Boolean: + { + bool b = aVal.GetBoolean(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0)); + xTab->setCell(nCol, nRow, pToken, 0, false); + } + break; + case ScMatValType::Value: + { + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal)); + xTab->setCell(nCol, nRow, pToken, 0, false); + } + break; + case ScMatValType::String: + { + ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(aVal.GetString())); + xTab->setCell(nCol, nRow, pToken, 0, false); + } + break; + default: + ; + } + } + } + + rFileId = nFileId; + rTabName = aSheet; + rRange = aRange; + return true; +} + +bool XclImpExtName::HasFormulaTokens() const +{ + return bool(mxArray); +} + +// Cached external cells ====================================================== + +XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) : + XclImpCachedValue( rStrm ), + maXclPos( rXclPos ) +{ +} + +// Sheet in an external document ============================================== + +XclImpSupbookTab::XclImpSupbookTab( const OUString& rTabName ) : + maTabName( rTabName ) +{ +} + +void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) +{ + XclImpCrnRef crnRef = std::make_shared<XclImpCrn>(rStrm, rXclPos); + maCrnList.push_back( crnRef ); +} + +void XclImpSupbookTab::LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable, + svl::SharedStringPool& rPool ) +{ + if (maCrnList.empty()) + return; + + for (const auto& rxCrn : maCrnList) + { + const XclImpCrn* const pCrn = rxCrn.get(); + const XclAddress& rAddr = pCrn->GetAddress(); + switch (pCrn->GetType()) + { + case EXC_CACHEDVAL_BOOL: + { + bool b = pCrn->GetBool(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false); + } + break; + case EXC_CACHEDVAL_DOUBLE: + { + double f = pCrn->GetValue(); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false); + } + break; + case EXC_CACHEDVAL_ERROR: + { + double fError = XclTools::ErrorToDouble( pCrn->GetXclError() ); + ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false); + } + break; + case EXC_CACHEDVAL_STRING: + { + svl::SharedString aSS( rPool.intern( pCrn->GetString())); + ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken( aSS)); + pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false); + } + break; + default: + ; + } + } +} + +// External document (SUPBOOK) ================================================ + +XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) : + XclImpRoot( rStrm.GetRoot() ), + meType( XclSupbookType::Unknown ), + mnSBTab( EXC_TAB_DELETED ) +{ + sal_uInt16 nSBTabCnt; + nSBTabCnt = rStrm.ReaduInt16(); + + if( rStrm.GetRecLeft() == 2 ) + { + switch( rStrm.ReaduInt16() ) + { + case EXC_SUPB_SELF: meType = XclSupbookType::Self; break; + case EXC_SUPB_ADDIN: meType = XclSupbookType::Addin; break; + default: OSL_FAIL( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" ); + } + return; + } + + OUString aEncUrl( rStrm.ReadUniString() ); + bool bSelf = false; + XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl ); + + if( maXclUrl.equalsIgnoreAsciiCase( "\010EUROTOOL.XLA" ) ) + { + meType = XclSupbookType::Eurotool; + maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) ); + } + else if( nSBTabCnt ) + { + meType = XclSupbookType::Extern; + + //assuming all empty strings with just len header of 0 + const size_t nMinRecordSize = sizeof(sal_Int16); + const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize; + if (nSBTabCnt > nMaxRecords) + { + SAL_WARN("sc", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nSBTabCnt << " claimed, truncating"); + nSBTabCnt = nMaxRecords; + } + + for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab ) + { + OUString aTabName( rStrm.ReadUniString() ); + maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( aTabName ) ); + } + } + else + { + meType = XclSupbookType::Special; + // create dummy list entry + maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) ); + } +} + +void XclImpSupbook::ReadXct( XclImpStream& rStrm ) +{ + rStrm.Ignore( 2 ); + mnSBTab = rStrm.ReaduInt16(); +} + +void XclImpSupbook::ReadCrn( XclImpStream& rStrm ) +{ + if (mnSBTab >= maSupbTabList.size()) + return; + XclImpSupbookTab& rSbTab = *maSupbTabList[mnSBTab]; + sal_uInt8 nXclColLast, nXclColFirst; + sal_uInt16 nXclRow; + nXclColLast = rStrm.ReaduInt8(); + nXclColFirst = rStrm.ReaduInt8(); + nXclRow = rStrm.ReaduInt16(); + + for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol ) + rSbTab.ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) ); +} + +void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + maExtNameList.push_back( std::make_unique<XclImpExtName>( *this, rStrm, meType, pFormulaConv ) ); +} + +const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const +{ + if (nXclIndex == 0) + { + SAL_WARN("sc", "XclImpSupbook::GetExternName - index must be >0"); + return nullptr; + } + if (meType == XclSupbookType::Self || nXclIndex > maExtNameList.size()) + return nullptr; + return maExtNameList[nXclIndex-1].get(); +} + +bool XclImpSupbook::GetLinkData( OUString& rApplic, OUString& rTopic ) const +{ + return (meType == XclSupbookType::Special) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl ); +} + +OUString XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const +{ + OSL_ENSURE( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" ); + const XclImpName* pName = (meType == XclSupbookType::Self) ? GetNameManager().GetName( nXclNameIdx ) : nullptr; + return (pName && pName->IsVBName()) ? pName->GetScName() : OUString(); +} + +OUString XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const +{ + if (nXtiTab >= maSupbTabList.size()) + return OUString(); + return maSupbTabList[nXtiTab]->GetTabName(); +} + +sal_uInt16 XclImpSupbook::GetTabCount() const +{ + return ulimit_cast<sal_uInt16>(maSupbTabList.size()); +} + +void XclImpSupbook::LoadCachedValues() +{ + if (meType != XclSupbookType::Extern || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell()) + return; + + OUString aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) ); + + ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager(); + sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl); + + for (auto& rxTab : maSupbTabList) + { + const OUString& rTabName = rxTab->GetTabName(); + ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true); + rxTab->LoadCachedValues( pCacheTable, GetSharedStringPool()); + pCacheTable->setWholeTableCached(); + } +} + +svl::SharedStringPool& XclImpSupbook::GetSharedStringPool() +{ + return GetDoc().GetSharedStringPool(); +} + +// Import link manager ======================================================== + +XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ) +{ +} + +void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm ) +{ + sal_uInt16 nXtiCount; + nXtiCount = rStrm.ReaduInt16(); + OSL_ENSURE( static_cast< std::size_t >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" ); + nXtiCount = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nXtiCount, rStrm.GetRecLeft() / 6 ) ); + + /* #i104057# A weird external XLS generator writes multiple EXTERNSHEET + records instead of only one as expected. Surprisingly, Excel seems to + insert the entries of the second record before the entries of the first + record. */ + XclImpXtiVector aNewEntries( nXtiCount ); + for( auto& rNewEntry : aNewEntries ) + { + if (!rStrm.IsValid()) + break; + rStrm >> rNewEntry; + } + maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() ); + + LoadCachedValues(); +} + +void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm ) +{ + maSupbookList.push_back( std::make_unique<XclImpSupbook>( rStrm ) ); +} + +void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm ) +{ + if( !maSupbookList.empty() ) + maSupbookList.back()->ReadXct( rStrm ); +} + +void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm ) +{ + if( !maSupbookList.empty() ) + maSupbookList.back()->ReadCrn( rStrm ); +} + +void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + if( !maSupbookList.empty() ) + maSupbookList.back()->ReadExternname( rStrm, pFormulaConv ); +} + +bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook && (pSupbook->GetType() == XclSupbookType::Self); +} + +bool XclImpLinkManagerImpl::GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const +{ + if( const XclImpXti* pXti = GetXti( nXtiIndex ) ) + { + if (!maSupbookList.empty() && (pXti->mnSupbook < maSupbookList.size()) ) + { + rnFirstScTab = pXti->mnSBTabFirst; + rnLastScTab = pXti->mnSBTabLast; + return true; + } + } + return false; +} + +const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook ? pSupbook->GetExternName( nExtName ) : nullptr; +} + +const OUString* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* p = GetSupbook( nXtiIndex ); + if (!p) + return nullptr; + return &p->GetXclUrl(); +} + +OUString XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const +{ + const XclImpSupbook* p = GetSupbook(nXti); + return p ? p->GetTabName(nXtiTab) : OUString(); +} + +bool XclImpLinkManagerImpl::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex ); + return pSupbook && pSupbook->GetLinkData( rApplic, rTopic ); +} + +OUString XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const +{ + const XclImpSupbook* pSupbook = GetSupbook( nExtSheet ); + return pSupbook ? pSupbook->GetMacroName( nExtName ) : OUString(); +} + +const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const +{ + return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : nullptr; +} + +const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const +{ + if ( maSupbookList.empty() ) + return nullptr; + const XclImpXti* pXti = GetXti( nXtiIndex ); + if (!pXti || pXti->mnSupbook >= maSupbookList.size()) + return nullptr; + return maSupbookList.at( pXti->mnSupbook ).get(); +} + +void XclImpLinkManagerImpl::LoadCachedValues() +{ + // Read all CRN records which can be accessed via XclImpSupbook, and store + // the cached values to the external reference manager. + for (auto& rxSupbook : maSupbookList) + rxSupbook->LoadCachedValues(); +} + +XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) : + XclImpRoot( rRoot ), + mxImpl( new XclImpLinkManagerImpl( rRoot ) ) +{ +} + +XclImpLinkManager::~XclImpLinkManager() +{ +} + +void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm ) +{ + mxImpl->ReadExternsheet( rStrm ); +} + +void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm ) +{ + mxImpl->ReadSupbook( rStrm ); +} + +void XclImpLinkManager::ReadXct( XclImpStream& rStrm ) +{ + mxImpl->ReadXct( rStrm ); +} + +void XclImpLinkManager::ReadCrn( XclImpStream& rStrm ) +{ + mxImpl->ReadCrn( rStrm ); +} + +void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv ) +{ + mxImpl->ReadExternname( rStrm, pFormulaConv ); +} + +bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const +{ + return mxImpl->IsSelfRef( nXtiIndex ); +} + +bool XclImpLinkManager::GetScTabRange( + SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex ); +} + +const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const +{ + return mxImpl->GetExternName( nXtiIndex, nExtName ); +} + +const OUString* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetSupbookUrl(nXtiIndex); +} + +OUString XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const +{ + return mxImpl->GetSupbookTabName(nXti, nXtiTab); +} + +bool XclImpLinkManager::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const +{ + return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex ); +} + +OUString XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const +{ + return mxImpl->GetMacroName( nExtSheet, nExtName ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |