use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xelink.cxx
blob520e06b8ab74c925bc0187387c935cb21a3ec2a3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <utility>
21 #include <xelink.hxx>
23 #include <algorithm>
24 #include <formula/errorcodes.hxx>
25 #include <oox/token/namespaces.hxx>
26 #include <oox/token/relationship.hxx>
27 #include <unotools/collatorwrapper.hxx>
28 #include <svl/numformat.hxx>
29 #include <svl/zforlist.hxx>
30 #include <sal/log.hxx>
31 #include <document.hxx>
32 #include <scextopt.hxx>
33 #include <externalrefmgr.hxx>
34 #include <tokenarray.hxx>
35 #include <xecontent.hxx>
36 #include <xeformula.hxx>
37 #include <xehelper.hxx>
38 #include <xllink.hxx>
39 #include <xltools.hxx>
41 #include <vector>
42 #include <memory>
43 #include <string_view>
45 using ::std::unique_ptr;
46 using ::std::vector;
47 using ::com::sun::star::uno::Any;
49 using namespace oox;
51 // *** Helper classes ***
53 // External names =============================================================
55 namespace {
57 /** This is a base class for any external name (i.e. add-in names or DDE links).
58 @descr Derived classes implement creation and export of the external names. */
59 class XclExpExtNameBase : public XclExpRecord, protected XclExpRoot
61 public:
62 /** @param nFlags The flags to export. */
63 explicit XclExpExtNameBase( const XclExpRoot& rRoot,
64 const OUString& rName, sal_uInt16 nFlags = 0 );
66 /** Returns the name string of the external name. */
67 const OUString& GetName() const { return maName; }
69 private:
70 /** Writes the start of the record that is equal in all EXTERNNAME records and calls WriteAddData(). */
71 virtual void WriteBody( XclExpStream& rStrm ) override;
72 /** Called to write additional data following the common record contents.
73 @descr Derived classes should overwrite this function to write their data. */
74 virtual void WriteAddData( XclExpStream& rStrm );
76 protected:
77 OUString maName; /// Calc name (title) of the external name.
78 XclExpStringRef mxName; /// Excel name (title) of the external name.
79 sal_uInt16 mnFlags; /// Flags for record export.
82 /** Represents an EXTERNNAME record for an add-in function name. */
83 class XclExpExtNameAddIn : public XclExpExtNameBase
85 public:
86 explicit XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName );
88 private:
89 /** Writes additional record contents. */
90 virtual void WriteAddData( XclExpStream& rStrm ) override;
93 /** Represents an EXTERNNAME record for a DDE link. */
94 class XclExpExtNameDde : public XclExpExtNameBase
96 public:
97 explicit XclExpExtNameDde( const XclExpRoot& rRoot, const OUString& rName,
98 sal_uInt16 nFlags, const ScMatrix* pResults = nullptr );
100 private:
101 /** Writes additional record contents. */
102 virtual void WriteAddData( XclExpStream& rStrm ) override;
104 private:
105 typedef std::shared_ptr< XclExpCachedMatrix > XclExpCachedMatRef;
106 XclExpCachedMatRef mxMatrix; /// Cached results of the DDE link.
109 class XclExpSupbook;
111 class XclExpExtName : public XclExpExtNameBase
113 public:
114 explicit XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, const OUString& rName,
115 const ScExternalRefCache::TokenArrayRef& rArray );
117 virtual void SaveXml(XclExpXmlStream& rStrm) override;
119 private:
120 /** Writes additional record contents. */
121 virtual void WriteAddData( XclExpStream& rStrm ) override;
123 private:
124 const XclExpSupbook& mrSupbook;
125 unique_ptr<ScTokenArray> mpArray;
128 // List of external names =====================================================
130 /** List of all external names of a sheet. */
131 class XclExpExtNameBuffer : public XclExpRecordBase, protected XclExpRoot
133 public:
134 explicit XclExpExtNameBuffer( const XclExpRoot& rRoot );
136 /** Inserts an add-in function name
137 @return The 1-based (Excel-like) list index of the name. */
138 sal_uInt16 InsertAddIn( const OUString& rName );
139 /** InsertEuroTool */
140 sal_uInt16 InsertEuroTool( const OUString& rName );
141 /** Inserts a DDE link.
142 @return The 1-based (Excel-like) list index of the DDE link. */
143 sal_uInt16 InsertDde( std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem );
145 sal_uInt16 InsertExtName( const XclExpSupbook& rSupbook, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
147 /** Writes the EXTERNNAME record list. */
148 virtual void Save( XclExpStream& rStrm ) override;
150 virtual void SaveXml(XclExpXmlStream& rStrm) override;
152 private:
153 /** Returns the 1-based (Excel-like) list index of the external name or 0, if not found. */
154 sal_uInt16 GetIndex( std::u16string_view rName ) const;
155 /** Appends the passed newly crested external name.
156 @return The 1-based (Excel-like) list index of the appended name. */
157 sal_uInt16 AppendNew( XclExpExtNameBase* pExtName );
159 XclExpRecordList< XclExpExtNameBase > maNameList; /// The list with all EXTERNNAME records.
162 // Cached external cells ======================================================
164 /** Stores the contents of a consecutive row of external cells (record CRN). */
165 class XclExpCrn : public XclExpRecord
167 public:
168 explicit XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue );
170 /** Returns true, if the passed value could be appended to this record. */
171 bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
173 /** Writes the row and child elements. */
174 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
176 private:
177 virtual void WriteBody( XclExpStream& rStrm ) override;
179 static void WriteBool( XclExpStream& rStrm, bool bValue );
180 static void WriteDouble( XclExpStream& rStrm, double fValue );
181 static void WriteString( XclExpStream& rStrm, const OUString& rValue );
182 static void WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode );
183 static void WriteEmpty( XclExpStream& rStrm );
185 private:
186 typedef ::std::vector< Any > CachedValues;
188 CachedValues maValues; /// All cached values.
189 SCCOL mnScCol; /// Column index of the first external cell.
190 SCROW mnScRow; /// Row index of the external cells.
193 class XclExpCrnList;
195 /** Represents the record XCT which is the header record of a CRN record list.
197 class XclExpXct : public XclExpRecordBase, protected XclExpRoot
199 public:
200 explicit XclExpXct( const XclExpRoot& rRoot,
201 const OUString& rTabName, sal_uInt16 nSBTab,
202 ScExternalRefCache::TableTypeRef xCacheTable );
204 /** Returns the external sheet name. */
205 const XclExpString& GetTabName() const { return maTabName; }
207 /** Stores all cells in the given range in the CRN list. */
208 void StoreCellRange( const ScRange& rRange );
210 void StoreCell_( const ScAddress& rCell );
211 void StoreCellRange_( const ScRange& rRange );
213 /** Writes the XCT and all CRN records. */
214 virtual void Save( XclExpStream& rStrm ) override;
216 /** Writes the sheetDataSet and child elements. */
217 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
219 private:
220 ScExternalRefCache::TableTypeRef mxCacheTable;
221 ScMarkData maUsedCells; /// Contains addresses of all stored cells.
222 ScRange maBoundRange; /// Bounding box of maUsedCells.
223 XclExpString maTabName; /// Sheet name of the external sheet.
224 sal_uInt16 mnSBTab; /// Referred sheet index in SUPBOOK record.
226 /** Build the internal representation of records to be saved as BIFF or OOXML. */
227 bool BuildCrnList( XclExpCrnList& rCrnRecs );
230 // External documents (EXTERNSHEET/SUPBOOK), base class =======================
232 /** Base class for records representing external sheets/documents.
234 In BIFF5/BIFF7, this record is the EXTERNSHEET record containing one sheet
235 of the own or an external document. In BIFF8, this record is the SUPBOOK
236 record representing the entire own or external document with all referenced
237 sheets.
239 class XclExpExternSheetBase : public XclExpRecord, protected XclExpRoot
241 public:
242 explicit XclExpExternSheetBase( const XclExpRoot& rRoot,
243 sal_uInt16 nRecId, sal_uInt32 nRecSize = 0 );
245 protected:
246 /** Creates and returns the list of EXTERNNAME records. */
247 XclExpExtNameBuffer& GetExtNameBuffer();
248 /** Writes the list of EXTERNNAME records. */
249 void WriteExtNameBuffer( XclExpStream& rStrm );
250 /** Writes the list of externalName elements. */
251 void WriteExtNameBufferXml( XclExpXmlStream& rStrm );
253 protected:
254 typedef std::shared_ptr< XclExpExtNameBuffer > XclExpExtNameBfrRef;
255 XclExpExtNameBfrRef mxExtNameBfr; /// List of EXTERNNAME records.
258 // External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
260 /** Represents an EXTERNSHEET record containing the URL and sheet name of a sheet.
261 @descr This class is used up to BIFF7 only, writing a BIFF8 EXTERNSHEET
262 record is implemented directly in the link manager. */
263 class XclExpExternSheet : public XclExpExternSheetBase
265 public:
266 /** Creates an EXTERNSHEET record containing a special code (i.e. own document or sheet). */
267 explicit XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode );
268 /** Creates an EXTERNSHEET record referring to an internal sheet. */
269 explicit XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName );
271 /** Finds or inserts an EXTERNNAME record for add-ins.
272 @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
273 sal_uInt16 InsertAddIn( const OUString& rName );
275 /** Writes the EXTERNSHEET and all EXTERNNAME, XCT and CRN records. */
276 virtual void Save( XclExpStream& rStrm ) override;
278 private:
279 /** Initializes the record data with the passed encoded URL. */
280 void Init( std::u16string_view rEncUrl );
281 /** Writes the contents of the EXTERNSHEET record. */
282 virtual void WriteBody( XclExpStream& rStrm ) override;
284 private:
285 XclExpString maTabName; /// The name of the sheet.
288 // External documents (SUPBOOK, BIFF8) ========================================
290 /** The SUPBOOK record contains data for an external document (URL, sheet names, external values). */
291 class XclExpSupbook : public XclExpExternSheetBase
293 public:
294 /** Creates a SUPBOOK record for internal references. */
295 explicit XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount );
296 /** Creates a SUPBOOK record for add-in functions. */
297 explicit XclExpSupbook( const XclExpRoot& rRoot );
298 /** EUROTOOL SUPBOOK */
299 explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType );
300 /** Creates a SUPBOOK record for an external document. */
301 explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl );
302 /** Creates a SUPBOOK record for a DDE link. */
303 explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic );
305 /** Returns true, if this SUPBOOK contains the passed URL of an external document. */
306 bool IsUrlLink( std::u16string_view rUrl ) const;
307 /** Returns true, if this SUPBOOK contains the passed DDE link. */
308 bool IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const;
309 /** Fills the passed reference log entry with the URL and sheet names. */
310 void FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
311 sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const;
313 /** Stores all cells in the given range in the CRN list of the specified SUPBOOK sheet. */
314 void StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab );
316 void StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell );
317 void StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange );
319 sal_uInt16 GetTabIndex( const OUString& rTabName ) const;
320 sal_uInt16 GetTabCount() const;
322 /** Inserts a new sheet name into the SUPBOOK and returns the SUPBOOK internal sheet index. */
323 sal_uInt16 InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable );
324 /** Finds or inserts an EXTERNNAME record for add-ins.
325 @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
326 sal_uInt16 InsertAddIn( const OUString& rName );
327 /** InsertEuroTool */
328 sal_uInt16 InsertEuroTool( const OUString& rName );
329 /** Finds or inserts an EXTERNNAME record for DDE links.
330 @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
331 sal_uInt16 InsertDde( const OUString& rItem );
333 sal_uInt16 InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
335 /** Get the type of record. */
336 XclSupbookType GetType() const;
338 /** For references to an external document, 1-based OOXML file ID. */
339 sal_uInt16 GetFileId() const;
341 /** For references to an external document. */
342 const OUString& GetUrl() const;
344 /** Writes the SUPBOOK and all EXTERNNAME, XCT and CRN records. */
345 virtual void Save( XclExpStream& rStrm ) override;
347 /** Writes the externalBook and all child elements. */
348 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
350 private:
351 /** Returns the sheet name inside of this SUPBOOK. */
352 const XclExpString* GetTabName( sal_uInt16 nSBTab ) const;
354 /** Writes the SUPBOOK record contents. */
355 virtual void WriteBody( XclExpStream& rStrm ) override;
357 private:
358 typedef XclExpRecordList< XclExpXct > XclExpXctList;
359 typedef XclExpXctList::RecordRefType XclExpXctRef;
361 XclExpXctList maXctList; /// List of XCT records (which contain CRN records).
362 OUString maUrl; /// URL of the external document or application name for DDE.
363 OUString maDdeTopic; /// Topic of a DDE link.
364 XclExpString maUrlEncoded; /// Document name encoded for Excel.
365 XclSupbookType meType; /// Type of this SUPBOOK record.
366 sal_uInt16 mnXclTabCount; /// Number of internal sheets.
367 sal_uInt16 mnFileId; /// 1-based external reference file ID for OOXML
370 // All SUPBOOKS in a document =================================================
372 /** This struct contains a sheet index range for 3D references.
373 @descr This reference consists of an index to a SUPBOOK record and indexes
374 to SUPBOOK sheet names. */
375 struct XclExpXti
377 sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
378 sal_uInt16 mnFirstSBTab; /// Index to the first sheet of the range in the SUPBOOK.
379 sal_uInt16 mnLastSBTab; /// Index to the last sheet of the range in the SUPBOOK.
381 explicit XclExpXti() : mnSupbook( 0 ), mnFirstSBTab( 0 ), mnLastSBTab( 0 ) {}
382 explicit XclExpXti( sal_uInt16 nSupbook, sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) :
383 mnSupbook( nSupbook ), mnFirstSBTab( nFirstSBTab ), mnLastSBTab( nLastSBTab ) {}
385 /** Writes this XTI structure (inside of the EXTERNSHEET record). */
386 void Save( XclExpStream& rStrm ) const
387 { rStrm << mnSupbook << mnFirstSBTab << mnLastSBTab; }
390 bool operator==( const XclExpXti& rLeft, const XclExpXti& rRight )
392 return
393 (rLeft.mnSupbook == rRight.mnSupbook) &&
394 (rLeft.mnFirstSBTab == rRight.mnFirstSBTab) &&
395 (rLeft.mnLastSBTab == rRight.mnLastSBTab);
398 /** Contains a list of all SUPBOOK records and index arrays of external sheets. */
399 class XclExpSupbookBuffer : public XclExpRecordBase, protected XclExpRoot
401 public:
402 explicit XclExpSupbookBuffer( const XclExpRoot& rRoot );
404 /** Finds SUPBOOK index and SUPBOOK sheet range from given Excel sheet range.
405 @return An XTI structure containing SUPBOOK and sheet indexes. */
406 XclExpXti GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
407 XclExpRefLogEntry* pRefLogEntry = nullptr ) const;
409 /** Stores all cells in the given range in a CRN record list. */
410 void StoreCellRange( const ScRange& rRange );
412 void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell );
413 void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
415 /** Finds or inserts an EXTERNNAME record for an add-in function name.
416 * @return an optional struct, containing [mnSupbook, mnExtName]
417 rnSupbook Returns the index of the SUPBOOK record which contains the DDE link.
418 rnExtName Returns the 1-based EXTERNNAME record index.
420 std::optional<XclExpSBIndex> InsertAddIn(const OUString& rName);
421 /** InsertEuroTool */
422 std::optional<XclExpSBIndex> InsertEuroTool(const OUString& rName);
423 /** Finds or inserts an EXTERNNAME record for DDE links.
424 * @return an optional struct, containing [mnSupbook, mnExtName]
425 * rnSupbook Returns the index of the SUPBOOK record which contains the DDE link.
426 rnExtName Returns the 1-based EXTERNNAME record index.
428 std::optional<XclExpSBIndex> InsertDde(const OUString& rApplic, const OUString& rTopic, const OUString& rItem);
430 std::optional<XclExpSBIndex> InsertExtName(const OUString& rUrl, const OUString& rName,
431 const ScExternalRefCache::TokenArrayRef& rArray);
433 XclExpXti GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
434 XclExpRefLogEntry* pRefLogEntry );
436 /** Writes all SUPBOOK records with their sub records. */
437 virtual void Save( XclExpStream& rStrm ) override;
439 /** Writes all externalBook elements with their child elements to OOXML. */
440 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
442 /** Whether we need to write externalReferences or not. */
443 bool HasExternalReferences() const;
445 private:
446 typedef XclExpRecordList< XclExpSupbook > XclExpSupbookList;
447 typedef XclExpSupbookList::RecordRefType XclExpSupbookRef;
449 private:
450 /** Searches for the SUPBOOK record containing the passed document URL.
451 @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
452 @return List index, if the SUPBOOK record exists (out-parameters are valid). */
453 std::optional<sal_uInt16> GetSupbookUrl(XclExpSupbookRef& rxSupbook,
454 std::u16string_view rUrl) const;
455 /** Searches for the SUPBOOK record containing the passed DDE link.
456 @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
457 @return List index, if the SUPBOOK record exists (out-parameters are valid). */
458 std::optional<sal_uInt16> GetSupbookDde(XclExpSupbookRef& rxSupbook,
459 std::u16string_view rApplic,
460 std::u16string_view rTopic) const;
462 /** Appends a new SUPBOOK to the list.
463 @return The list index of the SUPBOOK record. */
464 sal_uInt16 Append( XclExpSupbookRef const & xSupbook );
466 private:
467 XclExpSupbookList maSupbookList; /// List of all SUPBOOK records.
468 std::vector< XclExpSBIndex >
469 maSBIndexVec; /// SUPBOOK and sheet name index for each Excel sheet.
470 sal_uInt16 mnOwnDocSB; /// Index to SUPBOOK for own document.
471 sal_uInt16 mnAddInSB; /// Index to add-in SUPBOOK.
476 // Export link manager ========================================================
478 /** Abstract base class for implementation classes of the link manager. */
479 class XclExpLinkManagerImpl : protected XclExpRoot
481 public:
482 /** Derived classes search for an EXTSHEET structure for the given Calc sheet range. */
483 virtual void FindExtSheet( sal_uInt16& rnExtSheet,
484 sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
485 SCTAB nFirstScTab, SCTAB nLastScTab,
486 XclExpRefLogEntry* pRefLogEntry ) = 0;
487 /** Derived classes search for a special EXTERNSHEET index for the own document. */
488 virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) = 0;
490 virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
491 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
492 XclExpRefLogEntry* pRefLogEntry ) = 0;
494 /** Derived classes store all cells in the given range in a CRN record list. */
495 virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) = 0;
497 virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) = 0;
498 virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) = 0;
500 /** Derived classes find or insert an EXTERNNAME record for an add-in function name. */
501 virtual std::optional<XclExpSBIndex> InsertAddIn(const OUString& rName) = 0;
502 /** InsertEuroTool */
503 virtual std::optional<XclExpSBIndex> InsertEuroTool(const OUString& rName)
504 = 0;
506 /** Derived classes find or insert an EXTERNNAME record for DDE links. */
507 virtual std::optional<XclExpSBIndex> InsertDde(const OUString& rApplic, const OUString& rTopic, const OUString& rItem) = 0;
509 virtual std::optional<XclExpSBIndex> InsertExtName(const OUString& rUrl, const OUString& rName,
510 const ScExternalRefCache::TokenArrayRef& rArray)
511 = 0;
513 /** Derived classes write the entire link table to the passed stream. */
514 virtual void Save( XclExpStream& rStrm ) = 0;
516 /** Derived classes write the entire link table to the passed OOXML stream. */
517 virtual void SaveXml( XclExpXmlStream& rStrm ) = 0;
519 protected:
520 explicit XclExpLinkManagerImpl( const XclExpRoot& rRoot );
523 namespace {
525 /** Implementation of the link manager for BIFF5/BIFF7. */
526 class XclExpLinkManagerImpl5 : public XclExpLinkManagerImpl
528 public:
529 explicit XclExpLinkManagerImpl5( const XclExpRoot& rRoot );
531 virtual void FindExtSheet( sal_uInt16& rnExtSheet,
532 sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
533 SCTAB nFirstScTab, SCTAB nLastScTab,
534 XclExpRefLogEntry* pRefLogEntry ) override;
535 virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
537 virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
538 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
539 XclExpRefLogEntry* pRefLogEntry ) override;
541 virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
543 virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
544 virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
546 virtual std::optional<XclExpSBIndex> InsertAddIn(const OUString& rName) override;
548 /** InsertEuroTool */
549 virtual std::optional<XclExpSBIndex> InsertEuroTool(const OUString& rName) override;
551 virtual std::optional<XclExpSBIndex> InsertDde(const OUString& rApplic, const OUString& rTopic, const OUString& rItem) override;
553 virtual std::optional<XclExpSBIndex> InsertExtName(const OUString& rUrl, const OUString& rName,
554 const ScExternalRefCache::TokenArrayRef& rArray) override;
556 virtual void Save( XclExpStream& rStrm ) override;
558 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
560 private:
561 typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList;
562 typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef;
563 typedef ::std::map< SCTAB, sal_uInt16 > XclExpIntTabMap;
564 typedef ::std::map< sal_Unicode, sal_uInt16 > XclExpCodeMap;
566 private:
567 /** Returns the number of EXTERNSHEET records. */
568 sal_uInt16 GetExtSheetCount() const;
570 /** Appends an internal EXTERNSHEET record and returns the one-based index. */
571 sal_uInt16 AppendInternal( XclExpExtSheetRef const & xExtSheet );
572 /** Creates all EXTERNSHEET records for internal sheets on first call. */
573 void CreateInternal();
575 /** Returns the specified internal EXTERNSHEET record. */
576 XclExpExtSheetRef GetInternal( sal_uInt16 nExtSheet );
577 /** Returns the EXTERNSHEET index of an internal Calc sheet, or a deleted reference. */
578 XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab );
579 /** Finds or creates the EXTERNSHEET index of an internal special EXTERNSHEET. */
580 XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_Unicode cCode );
582 private:
583 XclExpExtSheetList maExtSheetList; /// List with EXTERNSHEET records.
584 XclExpIntTabMap maIntTabMap; /// Maps internal Calc sheets to EXTERNSHEET records.
585 XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records.
588 /** Implementation of the link manager for BIFF8 and OOXML. */
589 class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl
591 public:
592 explicit XclExpLinkManagerImpl8( const XclExpRoot& rRoot );
594 virtual void FindExtSheet( sal_uInt16& rnExtSheet,
595 sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
596 SCTAB nFirstScTab, SCTAB nLastScTab,
597 XclExpRefLogEntry* pRefLogEntry ) override;
598 virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
600 virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
601 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
602 XclExpRefLogEntry* pRefLogEntry ) override;
604 virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
606 virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
607 virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
609 virtual std::optional<XclExpSBIndex> InsertAddIn(const OUString& rName) override;
610 /** InsertEuroTool */
611 virtual std::optional<XclExpSBIndex> InsertEuroTool(const OUString& rName) override;
613 virtual std::optional<XclExpSBIndex> InsertDde(const OUString& rApplic, const OUString& rTopic, const OUString& rItem) override;
615 virtual std::optional<XclExpSBIndex> InsertExtName(const OUString& rUrl, const OUString& rName,
616 const ScExternalRefCache::TokenArrayRef& rArray) override;
618 virtual void Save( XclExpStream& rStrm ) override;
620 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
622 private:
623 /** Searches for or inserts a new XTI structure.
624 @return The 0-based list index of the XTI structure. */
625 sal_uInt16 InsertXti( const XclExpXti& rXti );
627 private:
629 XclExpSupbookBuffer maSBBuffer; /// List of all SUPBOOK records.
630 std::vector< XclExpXti > maXtiVec; /// List of XTI structures for the EXTERNSHEET record.
635 // *** Implementation ***
637 // Excel sheet indexes ========================================================
640 XclExpTabInfo::XclExpTabInfo( const XclExpRoot& rRoot ) :
641 XclExpRoot( rRoot ),
642 mnScCnt( 0 ),
643 mnXclCnt( 0 ),
644 mnXclExtCnt( 0 ),
645 mnXclSelCnt( 0 ),
646 mnDisplXclTab( 0 ),
647 mnFirstVisXclTab( 0 )
649 ScDocument& rDoc = GetDoc();
650 ScExtDocOptions& rDocOpt = GetExtDocOptions();
652 mnScCnt = rDoc.GetTableCount();
654 SCTAB nScTab;
655 SCTAB nFirstVisScTab = SCTAB_INVALID; // first visible sheet
656 SCTAB nFirstExpScTab = SCTAB_INVALID; // first exported sheet
658 // --- initialize the flags in the index buffer ---
660 maTabInfoVec.resize( mnScCnt );
661 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
663 // ignored sheets (skipped by export, with invalid Excel sheet index)
664 if( rDoc.IsScenario( nScTab ) )
666 SetFlag( nScTab, ExcTabBufFlags::Ignore );
669 // external sheets (skipped, but with valid Excel sheet index for ref's)
670 else if( rDoc.GetLinkMode( nScTab ) == ScLinkMode::VALUE )
672 SetFlag( nScTab, ExcTabBufFlags::Extern );
675 // exported sheets
676 else
678 // sheet name
679 rDoc.GetName( nScTab, maTabInfoVec[ nScTab ].maScName );
681 // remember first exported sheet
682 if( nFirstExpScTab == SCTAB_INVALID )
683 nFirstExpScTab = nScTab;
684 // remember first visible exported sheet
685 if( (nFirstVisScTab == SCTAB_INVALID) && rDoc.IsVisible( nScTab ) )
686 nFirstVisScTab = nScTab;
688 // sheet visible (only exported sheets)
689 SetFlag( nScTab, ExcTabBufFlags::Visible, rDoc.IsVisible( nScTab ) );
691 // sheet selected (only exported sheets)
692 if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nScTab ) )
693 SetFlag( nScTab, ExcTabBufFlags::Selected, pTabSett->mbSelected );
695 // sheet mirrored (only exported sheets)
696 SetFlag( nScTab, ExcTabBufFlags::Mirrored, rDoc.IsLayoutRTL( nScTab ) );
700 // --- visible/selected sheets ---
702 SCTAB nDisplScTab = rDocOpt.GetDocSettings().mnDisplTab;
704 // missing viewdata at embedded XLSX OLE objects
705 if (nDisplScTab == -1 )
706 nDisplScTab = rDoc.GetVisibleTab();
708 // find first visible exported sheet
709 if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
711 // no exportable visible sheet -> use first exportable sheet
712 nFirstVisScTab = nFirstExpScTab;
713 if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
715 // no exportable sheet at all -> use active sheet and export it
716 nFirstVisScTab = nDisplScTab;
717 SetFlag( nFirstVisScTab, ExcTabBufFlags::SkipMask, false ); // clear skip flags
719 SetFlag( nFirstVisScTab, ExcTabBufFlags::Visible ); // must be visible, even if originally hidden
722 // find currently displayed sheet
723 if( !IsExportTab( nDisplScTab ) ) // selected sheet not exported (i.e. scenario) -> use first visible
724 nDisplScTab = nFirstVisScTab;
725 SetFlag( nDisplScTab, ExcTabBufFlags::Visible | ExcTabBufFlags::Selected );
727 // number of selected sheets
728 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
729 if( IsSelectedTab( nScTab ) )
730 ++mnXclSelCnt;
732 // --- calculate resulting Excel sheet indexes ---
734 CalcXclIndexes();
735 mnFirstVisXclTab = GetXclTab( nFirstVisScTab );
736 mnDisplXclTab = GetXclTab( nDisplScTab );
738 // --- sorted vectors for index lookup ---
740 CalcSortedIndexes();
743 bool XclExpTabInfo::IsExportTab( SCTAB nScTab ) const
745 /* Check sheet index before to avoid assertion in GetFlag(). */
746 return (nScTab < mnScCnt && nScTab >= 0) && !GetFlag( nScTab, ExcTabBufFlags::SkipMask );
749 bool XclExpTabInfo::IsExternalTab( SCTAB nScTab ) const
751 /* Check sheet index before to avoid assertion (called from formula
752 compiler also for deleted references). */
753 return (nScTab < mnScCnt && nScTab >= 0) && GetFlag( nScTab, ExcTabBufFlags::Extern );
756 bool XclExpTabInfo::IsVisibleTab( SCTAB nScTab ) const
758 return GetFlag( nScTab, ExcTabBufFlags::Visible );
761 bool XclExpTabInfo::IsSelectedTab( SCTAB nScTab ) const
763 return GetFlag( nScTab, ExcTabBufFlags::Selected );
766 bool XclExpTabInfo::IsDisplayedTab( SCTAB nScTab ) const
768 OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
769 return GetXclTab( nScTab ) == mnDisplXclTab;
772 bool XclExpTabInfo::IsMirroredTab( SCTAB nScTab ) const
774 return GetFlag( nScTab, ExcTabBufFlags::Mirrored );
777 OUString XclExpTabInfo::GetScTabName( SCTAB nScTab ) const
779 OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
780 return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].maScName : OUString();
783 sal_uInt16 XclExpTabInfo::GetXclTab( SCTAB nScTab ) const
785 return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].mnXclTab : EXC_TAB_DELETED;
788 SCTAB XclExpTabInfo::GetRealScTab( SCTAB nSortedScTab ) const
790 OSL_ENSURE( nSortedScTab < mnScCnt && nSortedScTab >= 0, "XclExpTabInfo::GetRealScTab - sheet out of range" );
791 return (nSortedScTab < mnScCnt && nSortedScTab >= 0) ? maFromSortedVec[ nSortedScTab ] : SCTAB_INVALID;
794 bool XclExpTabInfo::GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const
796 OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::GetFlag - sheet out of range" );
797 return (nScTab < mnScCnt && nScTab >= 0) && (maTabInfoVec[ nScTab ].mnFlags & nFlags);
800 void XclExpTabInfo::SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet )
802 OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::SetFlag - sheet out of range" );
803 if( nScTab < mnScCnt && nScTab >= 0 )
805 if (bSet)
806 maTabInfoVec[ nScTab ].mnFlags |= nFlags;
807 else
808 maTabInfoVec[ nScTab ].mnFlags &= ~nFlags;
812 void XclExpTabInfo::CalcXclIndexes()
814 sal_uInt16 nXclTab = 0;
815 SCTAB nScTab = 0;
817 // --- pass 1: process regular sheets ---
818 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
820 if( IsExportTab( nScTab ) )
822 maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
823 ++nXclTab;
825 else
826 maTabInfoVec[ nScTab ].mnXclTab = EXC_TAB_DELETED;
828 mnXclCnt = nXclTab;
830 // --- pass 2: process external sheets (nXclTab continues) ---
831 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
833 if( IsExternalTab( nScTab ) )
835 maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
836 ++nXclTab;
837 ++mnXclExtCnt;
841 // result: first occur all exported sheets, followed by all external sheets
844 typedef ::std::pair< OUString, SCTAB > XclExpTabName;
846 namespace {
848 struct XclExpTabNameSort {
849 bool operator ()( const XclExpTabName& rArg1, const XclExpTabName& rArg2 )
851 // compare the sheet names only
852 return ScGlobal::GetCollator().compareString( rArg1.first, rArg2.first ) < 0;
858 void XclExpTabInfo::CalcSortedIndexes()
860 ScDocument& rDoc = GetDoc();
861 ::std::vector< XclExpTabName > aVec( mnScCnt );
862 SCTAB nScTab;
864 // fill with sheet names
865 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
867 rDoc.GetName( nScTab, aVec[ nScTab ].first );
868 aVec[ nScTab ].second = nScTab;
870 ::std::sort( aVec.begin(), aVec.end(), XclExpTabNameSort() );
872 // fill index vectors from sorted sheet name vector
873 maFromSortedVec.resize( mnScCnt );
874 maToSortedVec.resize( mnScCnt );
875 for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
877 maFromSortedVec[ nScTab ] = aVec[ nScTab ].second;
878 maToSortedVec[ aVec[ nScTab ].second ] = nScTab;
882 // External names =============================================================
884 XclExpExtNameBase::XclExpExtNameBase(
885 const XclExpRoot& rRoot, const OUString& rName, sal_uInt16 nFlags ) :
886 XclExpRecord( EXC_ID_EXTERNNAME ),
887 XclExpRoot( rRoot ),
888 maName( rName ),
889 mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
890 mnFlags( nFlags )
892 OSL_ENSURE( maName.getLength() <= 255, "XclExpExtNameBase::XclExpExtNameBase - string too long" );
893 SetRecSize( 6 + mxName->GetSize() );
896 void XclExpExtNameBase::WriteBody( XclExpStream& rStrm )
898 rStrm << mnFlags
899 << sal_uInt32( 0 )
900 << *mxName;
901 WriteAddData( rStrm );
904 void XclExpExtNameBase::WriteAddData( XclExpStream& /*rStrm*/ )
908 XclExpExtNameAddIn::XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName ) :
909 XclExpExtNameBase( rRoot, rName )
911 AddRecSize( 4 );
914 void XclExpExtNameAddIn::WriteAddData( XclExpStream& rStrm )
916 // write a #REF! error formula
917 rStrm << sal_uInt16( 2 ) << EXC_TOKID_ERR << EXC_ERR_REF;
920 XclExpExtNameDde::XclExpExtNameDde( const XclExpRoot& rRoot,
921 const OUString& rName, sal_uInt16 nFlags, const ScMatrix* pResults ) :
922 XclExpExtNameBase( rRoot, rName, nFlags )
924 if( pResults )
926 mxMatrix = std::make_shared<XclExpCachedMatrix>( *pResults );
927 AddRecSize( mxMatrix->GetSize() );
931 void XclExpExtNameDde::WriteAddData( XclExpStream& rStrm )
933 if( mxMatrix )
934 mxMatrix->Save( rStrm );
937 XclExpExtName::XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook,
938 const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) :
939 XclExpExtNameBase( rRoot, rName ),
940 mrSupbook(rSupbook),
941 mpArray(rArray->Clone())
945 void XclExpExtName::WriteAddData( XclExpStream& rStrm )
947 // Write only if it only has a single token that is either a cell or cell
948 // range address. Excel just writes '02 00 1C 17' for all the other types
949 // of external names.
951 using namespace ::formula;
954 if (mpArray->GetLen() != 1)
955 break;
957 const formula::FormulaToken* p = mpArray->FirstToken();
958 if (!p->IsExternalRef())
959 break;
961 switch (p->GetType())
963 case svExternalSingleRef:
965 const ScSingleRefData& rRef = *p->GetSingleRef();
966 if (rRef.IsTabRel())
967 break;
969 bool bColRel = rRef.IsColRel();
970 bool bRowRel = rRef.IsRowRel();
971 sal_uInt16 nCol = static_cast<sal_uInt16>(rRef.Col());
972 sal_uInt16 nRow = static_cast<sal_uInt16>(rRef.Row());
973 if (bColRel) nCol |= 0x4000;
974 if (bRowRel) nCol |= 0x8000;
976 OUString aTabName = p->GetString().getString();
977 sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
979 // size is always 9
980 rStrm << static_cast<sal_uInt16>(9);
981 // operator token (3A for cell reference)
982 rStrm << static_cast<sal_uInt8>(0x3A);
983 // cell address (Excel's address has 2 sheet IDs.)
984 rStrm << nSBTab << nSBTab << nRow << nCol;
985 return;
987 case svExternalDoubleRef:
989 const ScComplexRefData& rRef = *p->GetDoubleRef();
990 const ScSingleRefData& r1 = rRef.Ref1;
991 const ScSingleRefData& r2 = rRef.Ref2;
992 if (r1.IsTabRel() || r2.IsTabRel())
993 break;
995 sal_uInt16 nTab1 = r1.Tab();
996 sal_uInt16 nTab2 = r2.Tab();
997 bool bCol1Rel = r1.IsColRel();
998 bool bRow1Rel = r1.IsRowRel();
999 bool bCol2Rel = r2.IsColRel();
1000 bool bRow2Rel = r2.IsRowRel();
1002 sal_uInt16 nCol1 = static_cast<sal_uInt16>(r1.Col());
1003 sal_uInt16 nCol2 = static_cast<sal_uInt16>(r2.Col());
1004 sal_uInt16 nRow1 = static_cast<sal_uInt16>(r1.Row());
1005 sal_uInt16 nRow2 = static_cast<sal_uInt16>(r2.Row());
1006 if (bCol1Rel) nCol1 |= 0x4000;
1007 if (bRow1Rel) nCol1 |= 0x8000;
1008 if (bCol2Rel) nCol2 |= 0x4000;
1009 if (bRow2Rel) nCol2 |= 0x8000;
1011 OUString aTabName = p->GetString().getString();
1012 sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
1014 // size is always 13 (0x0D)
1015 rStrm << static_cast<sal_uInt16>(13);
1016 // operator token (3B for area reference)
1017 rStrm << static_cast<sal_uInt8>(0x3B);
1018 // range (area) address
1019 sal_uInt16 nSBTab2 = nSBTab + nTab2 - nTab1;
1020 rStrm << nSBTab << nSBTab2 << nRow1 << nRow2 << nCol1 << nCol2;
1021 return;
1023 default:
1024 ; // nothing
1027 while (false);
1029 // special value for #REF! (02 00 1C 17)
1030 rStrm << static_cast<sal_uInt16>(2) << EXC_TOKID_ERR << EXC_ERR_REF;
1033 void XclExpExtName::SaveXml(XclExpXmlStream& rStrm)
1035 sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
1037 /* TODO: mpArray contains external references. It doesn't cause any problems, but it's enough
1038 to export it without the external document identifier. */
1039 if (mpArray->GetLen())
1041 const OUString aFormula = XclXmlUtils::ToOUString(GetCompileFormulaContext(), ScAddress(0, 0, 0), mpArray.get());
1042 pExternalLink->startElement(XML_definedName,
1043 XML_name, maName.toUtf8(),
1044 XML_refersTo, aFormula.toUtf8(),
1045 XML_sheetId, nullptr);
1047 else
1049 pExternalLink->startElement(XML_definedName,
1050 XML_name, maName.toUtf8(),
1051 XML_refersTo, nullptr,
1052 XML_sheetId, nullptr);
1055 pExternalLink->endElement(XML_definedName);
1058 // List of external names =====================================================
1060 XclExpExtNameBuffer::XclExpExtNameBuffer( const XclExpRoot& rRoot ) :
1061 XclExpRoot( rRoot )
1065 sal_uInt16 XclExpExtNameBuffer::InsertAddIn( const OUString& rName )
1067 sal_uInt16 nIndex = GetIndex( rName );
1068 return nIndex ? nIndex : AppendNew( new XclExpExtNameAddIn( GetRoot(), rName ) );
1071 sal_uInt16 XclExpExtNameBuffer::InsertEuroTool( const OUString& rName )
1073 sal_uInt16 nIndex = GetIndex( rName );
1074 return nIndex ? nIndex : AppendNew( new XclExpExtNameBase( GetRoot(), rName ) );
1077 sal_uInt16 XclExpExtNameBuffer::InsertDde(
1078 std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem )
1080 sal_uInt16 nIndex = GetIndex( rItem );
1081 if( nIndex == 0 )
1083 size_t nPos;
1084 if( GetDoc().FindDdeLink( rApplic, rTopic, rItem, SC_DDE_IGNOREMODE, nPos ) )
1086 // create the leading 'StdDocumentName' EXTERNNAME record
1087 if( maNameList.IsEmpty() )
1088 AppendNew( new XclExpExtNameDde(
1089 GetRoot(), u"StdDocumentName"_ustr, EXC_EXTN_EXPDDE_STDDOC ) );
1091 // try to find DDE result array, but create EXTERNNAME record without them too
1092 const ScMatrix* pScMatrix = GetDoc().GetDdeLinkResultMatrix( nPos );
1093 nIndex = AppendNew( new XclExpExtNameDde( GetRoot(), rItem, EXC_EXTN_EXPDDE, pScMatrix ) );
1096 return nIndex;
1099 sal_uInt16 XclExpExtNameBuffer::InsertExtName( const XclExpSupbook& rSupbook,
1100 const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
1102 sal_uInt16 nIndex = GetIndex( rName );
1103 return nIndex ? nIndex : AppendNew( new XclExpExtName( GetRoot(), rSupbook, rName, rArray ) );
1106 void XclExpExtNameBuffer::Save( XclExpStream& rStrm )
1108 maNameList.Save( rStrm );
1111 void XclExpExtNameBuffer::SaveXml(XclExpXmlStream& rStrm)
1113 maNameList.SaveXml(rStrm);
1116 sal_uInt16 XclExpExtNameBuffer::GetIndex( std::u16string_view rName ) const
1118 for( size_t nPos = 0, nSize = maNameList.GetSize(); nPos < nSize; ++nPos )
1119 if( maNameList.GetRecord( nPos )->GetName() == rName )
1120 return static_cast< sal_uInt16 >( nPos + 1 );
1121 return 0;
1124 sal_uInt16 XclExpExtNameBuffer::AppendNew( XclExpExtNameBase* pExtName )
1126 size_t nSize = maNameList.GetSize();
1127 if( nSize < 0x7FFF )
1129 maNameList.AppendRecord( pExtName );
1130 return static_cast< sal_uInt16 >( nSize + 1 );
1132 return 0;
1135 // Cached external cells ======================================================
1137 XclExpCrn::XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ) :
1138 XclExpRecord( EXC_ID_CRN, 4 ),
1139 mnScCol( nScCol ),
1140 mnScRow( nScRow )
1142 maValues.push_back( rValue );
1145 bool XclExpCrn::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
1147 if( (nScRow != mnScRow) || (nScCol != static_cast< SCCOL >( mnScCol + maValues.size() )) )
1148 return false;
1149 maValues.push_back( rValue );
1150 return true;
1153 void XclExpCrn::WriteBody( XclExpStream& rStrm )
1155 rStrm << static_cast< sal_uInt8 >( mnScCol + maValues.size() - 1 )
1156 << static_cast< sal_uInt8 >( mnScCol )
1157 << static_cast< sal_uInt16 >( mnScRow );
1158 for( const auto& rValue : maValues )
1160 if( rValue.has< bool >() )
1161 WriteBool( rStrm, rValue.get< bool >() );
1162 else if( rValue.has< double >() )
1163 WriteDouble( rStrm, rValue.get< double >() );
1164 else if( rValue.has< OUString >() )
1165 WriteString( rStrm, rValue.get< OUString >() );
1166 else
1167 WriteEmpty( rStrm );
1171 void XclExpCrn::WriteBool( XclExpStream& rStrm, bool bValue )
1173 rStrm << EXC_CACHEDVAL_BOOL << sal_uInt8( bValue ? 1 : 0);
1174 rStrm.WriteZeroBytes( 7 );
1177 void XclExpCrn::WriteDouble( XclExpStream& rStrm, double fValue )
1179 if( !std::isfinite( fValue ) )
1181 FormulaError nScError = GetDoubleErrorValue(fValue);
1182 WriteError( rStrm, XclTools::GetXclErrorCode( nScError ) );
1184 else
1186 rStrm << EXC_CACHEDVAL_DOUBLE << fValue;
1190 void XclExpCrn::WriteString( XclExpStream& rStrm, const OUString& rValue )
1192 rStrm << EXC_CACHEDVAL_STRING << XclExpString( rValue );
1195 void XclExpCrn::WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode )
1197 rStrm << EXC_CACHEDVAL_ERROR << nErrCode;
1198 rStrm.WriteZeroBytes( 7 );
1201 void XclExpCrn::WriteEmpty( XclExpStream& rStrm )
1203 rStrm << EXC_CACHEDVAL_EMPTY;
1204 rStrm.WriteZeroBytes( 8 );
1207 void XclExpCrn::SaveXml( XclExpXmlStream& rStrm )
1209 sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
1211 pFS->startElement(XML_row, XML_r, OString::number(mnScRow + 1));
1213 ScAddress aAdr( mnScCol, mnScRow, 0); // Tab number doesn't matter
1214 for( const auto& rValue : maValues )
1216 bool bCloseCell = true;
1217 if( rValue.has< double >() )
1219 double fVal = rValue.get< double >();
1220 if (std::isfinite( fVal))
1222 // t='n' is omitted
1223 pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(aAdr)));
1224 pFS->startElement(XML_v);
1225 pFS->write( fVal );
1227 else
1229 pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(aAdr)), XML_t, "e");
1230 pFS->startElement(XML_v);
1231 pFS->write( "#VALUE!" ); // OOXTODO: support other error values
1234 else if( rValue.has< OUString >() )
1236 pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(aAdr)), XML_t, "str");
1237 pFS->startElement(XML_v);
1238 pFS->write( rValue.get< OUString >() );
1240 else if( rValue.has< bool >() )
1242 pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(aAdr)), XML_t, "b");
1243 pFS->startElement(XML_v);
1244 pFS->write( rValue.get< bool >() ? "1" : "0" );
1246 // OOXTODO: error type cell t='e'
1247 else
1249 // Empty/blank cell not stored, only aAdr is incremented.
1250 bCloseCell = false;
1252 if (bCloseCell)
1254 pFS->endElement(XML_v);
1255 pFS->endElement(XML_cell);
1257 aAdr.IncCol();
1260 pFS->endElement( XML_row);
1263 // Cached cells of a sheet ====================================================
1265 XclExpXct::XclExpXct( const XclExpRoot& rRoot, const OUString& rTabName,
1266 sal_uInt16 nSBTab, ScExternalRefCache::TableTypeRef xCacheTable ) :
1267 XclExpRoot( rRoot ),
1268 mxCacheTable(std::move( xCacheTable )),
1269 maUsedCells( rRoot.GetDoc().GetSheetLimits() ),
1270 maBoundRange( ScAddress::INITIALIZE_INVALID ),
1271 maTabName( rTabName ),
1272 mnSBTab( nSBTab )
1276 void XclExpXct::StoreCellRange( const ScRange& rRange )
1278 // #i70418# restrict size of external range to prevent memory overflow
1279 if( (rRange.aEnd.Col() - rRange.aStart.Col()) * (rRange.aEnd.Row() - rRange.aStart.Row()) > 1024 )
1280 return;
1282 maUsedCells.SetMultiMarkArea( rRange );
1283 maBoundRange.ExtendTo( rRange );
1286 void XclExpXct::StoreCell_( const ScAddress& rCell )
1288 maUsedCells.SetMultiMarkArea( ScRange( rCell ) );
1289 maBoundRange.ExtendTo( ScRange( rCell ) );
1292 void XclExpXct::StoreCellRange_( const ScRange& rRange )
1294 maUsedCells.SetMultiMarkArea( rRange );
1295 maBoundRange.ExtendTo( rRange );
1298 namespace {
1300 class XclExpCrnList : public XclExpRecordList< XclExpCrn >
1302 public:
1303 /** Inserts the passed value into an existing or new CRN record.
1304 @return True = value inserted successfully, false = CRN list is full. */
1305 bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
1308 bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
1310 RecordRefType xLastRec = GetLastRecord();
1311 if( xLastRec && xLastRec->InsertValue( nScCol, nScRow, rValue ) )
1312 return true;
1313 if( GetSize() == SAL_MAX_UINT16 )
1314 return false;
1315 AppendNewRecord( new XclExpCrn( nScCol, nScRow, rValue ) );
1316 return true;
1319 } // namespace
1321 bool XclExpXct::BuildCrnList( XclExpCrnList& rCrnRecs )
1323 if( !mxCacheTable )
1324 return false;
1326 /* Get the range of used rows in the cache table. This may help to
1327 optimize building the CRN record list if the cache table does not
1328 contain all referred cells, e.g. if big empty ranges are used in the
1329 formulas. */
1330 ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange();
1331 if( aRowRange.first >= aRowRange.second )
1332 return false;
1334 /* Crop the bounding range of used cells in this table to Excel limits.
1335 Return if there is no external cell inside these limits. */
1336 if( !GetAddressConverter().ValidateRange( maBoundRange, false ) )
1337 return false;
1339 /* Find the resulting row range that needs to be processed. */
1340 SCROW nScRow1 = ::std::max( aRowRange.first, maBoundRange.aStart.Row() );
1341 SCROW nScRow2 = ::std::min( aRowRange.second - 1, maBoundRange.aEnd.Row() );
1342 if( nScRow1 > nScRow2 )
1343 return false;
1345 /* Build and collect all CRN records before writing the XCT record. This
1346 is needed to determine the total number of CRN records which must be
1347 known when writing the XCT record (possibly encrypted, so seeking the
1348 output stream back after writing the CRN records is not an option). */
1349 SvNumberFormatter& rFormatter = GetFormatter();
1350 bool bValid = true;
1351 for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow )
1353 ::std::pair< SCCOL, SCCOL > aColRange = mxCacheTable->getColRange( nScRow );
1354 const SCCOL nScEnd = ::std::min( aColRange.second, GetDoc().GetSheetLimits().GetMaxColCount() );
1355 for( SCCOL nScCol = aColRange.first; bValid && (nScCol < nScEnd); ++nScCol )
1357 if( maUsedCells.IsCellMarked( nScCol, nScRow, true ) )
1359 sal_uInt32 nScNumFmt = 0;
1360 ScExternalRefCache::TokenRef xToken = mxCacheTable->getCell( nScCol, nScRow, &nScNumFmt );
1361 using namespace ::formula;
1362 if( xToken )
1363 switch( xToken->GetType() )
1365 case svDouble:
1366 bValid = (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) ?
1367 rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) :
1368 rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) );
1369 break;
1370 case svString:
1371 // do not save empty strings (empty cells) to cache
1372 if( !xToken->GetString().isEmpty() )
1373 bValid = rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) );
1374 break;
1375 default:
1376 break;
1381 return true;
1384 void XclExpXct::Save( XclExpStream& rStrm )
1386 XclExpCrnList aCrnRecs;
1387 if (!BuildCrnList( aCrnRecs))
1388 return;
1390 // write the XCT record and the list of CRN records
1391 rStrm.StartRecord( EXC_ID_XCT, 4 );
1392 rStrm << static_cast< sal_uInt16 >( aCrnRecs.GetSize() ) << mnSBTab;
1393 rStrm.EndRecord();
1394 aCrnRecs.Save( rStrm );
1397 void XclExpXct::SaveXml( XclExpXmlStream& rStrm )
1399 XclExpCrnList aCrnRecs;
1401 sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
1403 bool bValid = BuildCrnList( aCrnRecs);
1404 pFS->startElement(XML_sheetData, XML_sheetId, OString::number(mnSBTab));
1405 if (bValid)
1407 // row elements
1408 aCrnRecs.SaveXml( rStrm );
1410 pFS->endElement( XML_sheetData);
1413 // External documents (EXTERNSHEET/SUPBOOK), base class =======================
1415 XclExpExternSheetBase::XclExpExternSheetBase( const XclExpRoot& rRoot, sal_uInt16 nRecId, sal_uInt32 nRecSize ) :
1416 XclExpRecord( nRecId, nRecSize ),
1417 XclExpRoot( rRoot )
1421 XclExpExtNameBuffer& XclExpExternSheetBase::GetExtNameBuffer()
1423 if( !mxExtNameBfr )
1424 mxExtNameBfr = std::make_shared<XclExpExtNameBuffer>( GetRoot() );
1425 return *mxExtNameBfr;
1428 void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm )
1430 if( mxExtNameBfr )
1431 mxExtNameBfr->Save( rStrm );
1434 void XclExpExternSheetBase::WriteExtNameBufferXml( XclExpXmlStream& rStrm )
1436 if( mxExtNameBfr )
1437 mxExtNameBfr->SaveXml( rStrm );
1440 // External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
1442 XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ) :
1443 XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
1445 Init( OUStringChar(cCode) );
1448 XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName ) :
1449 XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
1451 // reference to own sheet: \03<sheetname>
1452 Init(Concat2View(OUStringChar(EXC_EXTSH_TABNAME) + rTabName));
1455 void XclExpExternSheet::Save( XclExpStream& rStrm )
1457 // EXTERNSHEET record
1458 XclExpRecord::Save( rStrm );
1459 // EXTERNNAME records
1460 WriteExtNameBuffer( rStrm );
1463 void XclExpExternSheet::Init( std::u16string_view rEncUrl )
1465 OSL_ENSURE_BIFF( GetBiff() <= EXC_BIFF5 );
1466 maTabName.AssignByte( rEncUrl, GetTextEncoding(), XclStrFlags::EightBitLength );
1467 SetRecSize( maTabName.GetSize() );
1470 sal_uInt16 XclExpExternSheet::InsertAddIn( const OUString& rName )
1472 return GetExtNameBuffer().InsertAddIn( rName );
1475 void XclExpExternSheet::WriteBody( XclExpStream& rStrm )
1477 sal_uInt8 nNameSize = static_cast< sal_uInt8 >( maTabName.Len() );
1478 // special case: reference to own sheet (starting with '\03') needs wrong string length
1479 if( maTabName.GetChar( 0 ) == EXC_EXTSH_TABNAME )
1480 --nNameSize;
1481 rStrm << nNameSize;
1482 maTabName.WriteBuffer( rStrm );
1485 // External document (SUPBOOK, BIFF8) =========================================
1487 XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) :
1488 XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
1489 meType( XclSupbookType::Self ),
1490 mnXclTabCount( nXclTabCount ),
1491 mnFileId( 0 )
1495 XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) :
1496 XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
1497 meType( XclSupbookType::Addin ),
1498 mnXclTabCount( 1 ),
1499 mnFileId( 0 )
1503 XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType ) :
1504 XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
1505 maUrl( rUrl ),
1506 maUrlEncoded( rUrl ),
1507 meType( XclSupbookType::Eurotool ),
1508 mnXclTabCount( 0 ),
1509 mnFileId( 0 )
1511 SetRecSize( 2 + maUrlEncoded.GetSize() );
1514 XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl ) :
1515 XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
1516 maUrl( rUrl ),
1517 maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ),
1518 meType( XclSupbookType::Extern ),
1519 mnXclTabCount( 0 ),
1520 mnFileId( 0 )
1522 SetRecSize( 2 + maUrlEncoded.GetSize() );
1524 // We need to create all tables up front to ensure the correct table order.
1525 ScExternalRefManager* pRefMgr = rRoot.GetDoc().GetExternalRefManager();
1526 sal_uInt16 nFileId = pRefMgr->getExternalFileId( rUrl );
1527 mnFileId = nFileId + 1;
1528 ScfStringVec aTabNames;
1529 pRefMgr->getAllCachedTableNames( nFileId, aTabNames );
1530 size_t nTabIndex = 0;
1531 for( const auto& rTabName : aTabNames )
1533 InsertTabName( rTabName, pRefMgr->getCacheTable( nFileId, nTabIndex ) );
1534 ++nTabIndex;
1538 XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic ) :
1539 XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
1540 maUrl( rApplic ),
1541 maDdeTopic( rTopic ),
1542 maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ),
1543 meType( XclSupbookType::Special ),
1544 mnXclTabCount( 0 ),
1545 mnFileId( 0 )
1547 SetRecSize( 2 + maUrlEncoded.GetSize() );
1550 bool XclExpSupbook::IsUrlLink( std::u16string_view rUrl ) const
1552 return (meType == XclSupbookType::Extern || meType == XclSupbookType::Eurotool) && (maUrl == rUrl);
1555 bool XclExpSupbook::IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const
1557 return (meType == XclSupbookType::Special) && (maUrl == rApplic) && (maDdeTopic == rTopic);
1560 void XclExpSupbook::FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
1561 sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const
1563 rRefLogEntry.mpUrl = maUrlEncoded.IsEmpty() ? nullptr : &maUrlEncoded;
1564 rRefLogEntry.mpFirstTab = GetTabName( nFirstSBTab );
1565 rRefLogEntry.mpLastTab = GetTabName( nLastSBTab );
1568 void XclExpSupbook::StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab )
1570 if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
1571 pXct->StoreCellRange( rRange );
1574 void XclExpSupbook::StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell )
1576 if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
1577 pXct->StoreCell_( rCell );
1580 void XclExpSupbook::StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange )
1582 // multi-table range is not allowed!
1583 if( rRange.aStart.Tab() == rRange.aEnd.Tab() )
1584 if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
1585 pXct->StoreCellRange_( rRange );
1588 sal_uInt16 XclExpSupbook::GetTabIndex( const OUString& rTabName ) const
1590 XclExpString aXclName(rTabName);
1591 size_t nSize = maXctList.GetSize();
1592 for (size_t i = 0; i < nSize; ++i)
1594 XclExpXctRef aRec = maXctList.GetRecord(i);
1595 if (aXclName == aRec->GetTabName())
1596 return ulimit_cast<sal_uInt16>(i);
1598 return EXC_NOTAB;
1601 sal_uInt16 XclExpSupbook::GetTabCount() const
1603 return ulimit_cast<sal_uInt16>(maXctList.GetSize());
1606 sal_uInt16 XclExpSupbook::InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable )
1608 SAL_WARN_IF( meType != XclSupbookType::Extern, "sc.filter", "Don't insert sheet names here" );
1609 sal_uInt16 nSBTab = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
1610 XclExpXctRef xXct = new XclExpXct( GetRoot(), rTabName, nSBTab, xCacheTable );
1611 AddRecSize( xXct->GetTabName().GetSize() );
1612 maXctList.AppendRecord( xXct );
1613 return nSBTab;
1616 sal_uInt16 XclExpSupbook::InsertAddIn( const OUString& rName )
1618 return GetExtNameBuffer().InsertAddIn( rName );
1621 sal_uInt16 XclExpSupbook::InsertEuroTool( const OUString& rName )
1623 return GetExtNameBuffer().InsertEuroTool( rName );
1626 sal_uInt16 XclExpSupbook::InsertDde( const OUString& rItem )
1628 return GetExtNameBuffer().InsertDde( maUrl, maDdeTopic, rItem );
1631 sal_uInt16 XclExpSupbook::InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
1633 return GetExtNameBuffer().InsertExtName(*this, rName, rArray);
1636 XclSupbookType XclExpSupbook::GetType() const
1638 return meType;
1641 sal_uInt16 XclExpSupbook::GetFileId() const
1643 return mnFileId;
1646 const OUString& XclExpSupbook::GetUrl() const
1648 return maUrl;
1651 void XclExpSupbook::Save( XclExpStream& rStrm )
1653 // SUPBOOK record
1654 XclExpRecord::Save( rStrm );
1655 // XCT record, CRN records
1656 maXctList.Save( rStrm );
1657 // EXTERNNAME records
1658 WriteExtNameBuffer( rStrm );
1661 void XclExpSupbook::SaveXml( XclExpXmlStream& rStrm )
1663 sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
1665 // Add relation for this stream, e.g. xl/externalLinks/_rels/externalLink1.xml.rels
1666 sal_uInt16 nLevel = 0;
1667 bool bRel = true;
1669 // BuildFileName delete ../ and convert them to nLevel
1670 // but addrelation needs ../ instead of nLevel, so we have to convert it back
1671 OUString sFile = XclExpHyperlink::BuildFileName(nLevel, bRel, maUrl, GetRoot(), true);
1672 while (nLevel > 0)
1674 sFile = "../" + sFile;
1675 --nLevel;
1678 OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(),
1679 oox::getRelationship(Relationship::EXTERNALLINKPATH), sFile, true );
1681 pExternalLink->startElement( XML_externalLink,
1682 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8());
1684 pExternalLink->startElement( XML_externalBook,
1685 FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
1686 FSNS(XML_r, XML_id), sId.toUtf8());
1688 if (!maXctList.IsEmpty())
1690 pExternalLink->startElement(XML_sheetNames);
1691 for (size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos)
1693 pExternalLink->singleElement(XML_sheetName,
1694 XML_val, XclXmlUtils::ToOString(maXctList.GetRecord(nPos)->GetTabName()));
1696 pExternalLink->endElement( XML_sheetNames);
1700 if (mxExtNameBfr)
1702 pExternalLink->startElement(XML_definedNames);
1703 // externalName elements
1704 WriteExtNameBufferXml( rStrm );
1705 pExternalLink->endElement(XML_definedNames);
1708 if (!maXctList.IsEmpty())
1710 pExternalLink->startElement(XML_sheetDataSet);
1712 // sheetData elements
1713 maXctList.SaveXml( rStrm );
1715 pExternalLink->endElement( XML_sheetDataSet);
1718 pExternalLink->endElement( XML_externalBook);
1719 pExternalLink->endElement( XML_externalLink);
1722 const XclExpString* XclExpSupbook::GetTabName( sal_uInt16 nSBTab ) const
1724 XclExpXctRef xXct = maXctList.GetRecord( nSBTab );
1725 return xXct ? &xXct->GetTabName() : nullptr;
1728 void XclExpSupbook::WriteBody( XclExpStream& rStrm )
1730 switch( meType )
1732 case XclSupbookType::Self:
1733 rStrm << mnXclTabCount << EXC_SUPB_SELF;
1734 break;
1735 case XclSupbookType::Extern:
1736 case XclSupbookType::Special:
1737 case XclSupbookType::Eurotool:
1739 sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
1740 rStrm << nCount << maUrlEncoded;
1742 for( size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos )
1743 rStrm << maXctList.GetRecord( nPos )->GetTabName();
1745 break;
1746 case XclSupbookType::Addin:
1747 rStrm << mnXclTabCount << EXC_SUPB_ADDIN;
1748 break;
1749 default:
1750 SAL_WARN( "sc.filter", "Unhandled SUPBOOK type " << meType);
1754 // All SUPBOOKS in a document =================================================
1756 XclExpSupbookBuffer::XclExpSupbookBuffer( const XclExpRoot& rRoot ) :
1757 XclExpRoot( rRoot ),
1758 mnOwnDocSB( SAL_MAX_UINT16 ),
1759 mnAddInSB( SAL_MAX_UINT16 )
1761 XclExpTabInfo& rTabInfo = GetTabInfo();
1762 sal_uInt16 nXclCnt = rTabInfo.GetXclTabCount();
1763 sal_uInt16 nCodeCnt = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
1764 size_t nCount = nXclCnt + rTabInfo.GetXclExtTabCount();
1766 OSL_ENSURE( nCount > 0, "XclExpSupbookBuffer::XclExpSupbookBuffer - no sheets to export" );
1767 if( nCount )
1769 maSBIndexVec.resize( nCount );
1771 // self-ref SUPBOOK first of list
1772 XclExpSupbookRef xSupbook = new XclExpSupbook( GetRoot(), ::std::max( nXclCnt, nCodeCnt ) );
1773 mnOwnDocSB = Append( xSupbook );
1774 for( sal_uInt16 nXclTab = 0; nXclTab < nXclCnt; ++nXclTab )
1775 maSBIndexVec[ nXclTab ].Set( mnOwnDocSB, nXclTab );
1779 XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
1780 XclExpRefLogEntry* pRefLogEntry ) const
1782 XclExpXti aXti;
1783 size_t nSize = maSBIndexVec.size();
1784 if( (nFirstXclTab < nSize) && (nLastXclTab < nSize) )
1786 // index of the SUPBOOK record
1787 aXti.mnSupbook = maSBIndexVec[ nFirstXclTab ].mnSupbook;
1789 // all sheets in the same supbook?
1790 bool bSameSB = true;
1791 for( sal_uInt16 nXclTab = nFirstXclTab + 1; bSameSB && (nXclTab <= nLastXclTab); ++nXclTab )
1793 bSameSB = maSBIndexVec[ nXclTab ].mnSupbook == aXti.mnSupbook;
1794 if( !bSameSB )
1795 nLastXclTab = nXclTab - 1;
1797 aXti.mnFirstSBTab = maSBIndexVec[ nFirstXclTab ].mnSBTab;
1798 aXti.mnLastSBTab = maSBIndexVec[ nLastXclTab ].mnSBTab;
1800 // fill external reference log entry (for change tracking)
1801 if( pRefLogEntry )
1803 pRefLogEntry->mnFirstXclTab = nFirstXclTab;
1804 pRefLogEntry->mnLastXclTab = nLastXclTab;
1805 XclExpSupbookRef xSupbook = maSupbookList.GetRecord( aXti.mnSupbook );
1806 if( xSupbook )
1807 xSupbook->FillRefLogEntry( *pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab );
1810 else
1812 // special range, i.e. for deleted sheets or add-ins
1813 aXti.mnSupbook = mnOwnDocSB;
1814 aXti.mnFirstSBTab = nFirstXclTab;
1815 aXti.mnLastSBTab = nLastXclTab;
1818 return aXti;
1821 void XclExpSupbookBuffer::StoreCellRange( const ScRange& rRange )
1823 sal_uInt16 nXclTab = GetTabInfo().GetXclTab( rRange.aStart.Tab() );
1824 if( nXclTab < maSBIndexVec.size() )
1826 const XclExpSBIndex& rSBIndex = maSBIndexVec[ nXclTab ];
1827 XclExpSupbookRef xSupbook = maSupbookList.GetRecord( rSBIndex.mnSupbook );
1828 OSL_ENSURE( xSupbook , "XclExpSupbookBuffer::StoreCellRange - missing SUPBOOK record" );
1829 if( xSupbook )
1830 xSupbook->StoreCellRange( rRange, rSBIndex.mnSBTab );
1834 namespace {
1836 class FindSBIndexEntry
1838 public:
1839 explicit FindSBIndexEntry(sal_uInt16 nSupbookId, sal_uInt16 nTabId) :
1840 mnSupbookId(nSupbookId), mnTabId(nTabId) {}
1842 bool operator()(const XclExpSBIndex& r) const
1844 return mnSupbookId == r.mnSupbook && mnTabId == r.mnSBTab;
1847 private:
1848 sal_uInt16 mnSupbookId;
1849 sal_uInt16 mnTabId;
1854 void XclExpSupbookBuffer::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell )
1856 ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
1857 const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
1858 if (!pUrl)
1859 return;
1861 XclExpSupbookRef xSupbook;
1862 auto nSupbookId = GetSupbookUrl(xSupbook, *pUrl);
1863 if (!nSupbookId)
1865 xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
1866 nSupbookId = Append(xSupbook);
1869 sal_uInt16 nSheetId = xSupbook->GetTabIndex(rTabName);
1870 if (nSheetId == EXC_NOTAB)
1871 // specified table name not found in this SUPBOOK.
1872 return;
1874 FindSBIndexEntry f(*nSupbookId, nSheetId);
1875 if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
1877 maSBIndexVec.emplace_back();
1878 XclExpSBIndex& r = maSBIndexVec.back();
1879 r.mnSupbook = *nSupbookId;
1880 r.mnSBTab = nSheetId;
1883 xSupbook->StoreCell_(nSheetId, rCell);
1886 void XclExpSupbookBuffer::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
1888 ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
1889 const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
1890 if (!pUrl)
1891 return;
1893 XclExpSupbookRef xSupbook;
1894 auto nSupbookId = GetSupbookUrl(xSupbook, *pUrl);
1895 if (!nSupbookId)
1897 xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
1898 nSupbookId = Append(xSupbook);
1901 SCTAB nTabCount = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
1903 // If this is a multi-table range, get token for each table.
1904 using namespace ::formula;
1905 SCTAB aMatrixListSize = 0;
1907 // This is a new'ed instance, so we must manage its life cycle here.
1908 ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, rRange, nullptr);
1909 if (!pArray)
1910 return;
1912 FormulaTokenArrayPlainIterator aIter(*pArray);
1913 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
1915 if (p->GetType() == svMatrix)
1916 ++aMatrixListSize;
1917 else if (p->GetOpCode() != ocSep)
1919 // This is supposed to be ocSep!!!
1920 return;
1924 if (aMatrixListSize != nTabCount)
1926 // matrix size mismatch!
1927 return;
1930 sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
1932 ScRange aRange(rRange);
1933 aRange.aStart.SetTab(0);
1934 aRange.aEnd.SetTab(0);
1935 for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
1937 sal_uInt16 nSheetId = nFirstSheetId + static_cast<sal_uInt16>(nTab);
1938 FindSBIndexEntry f(*nSupbookId, nSheetId);
1939 if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
1941 maSBIndexVec.emplace_back();
1942 XclExpSBIndex& r = maSBIndexVec.back();
1943 r.mnSupbook = *nSupbookId;
1944 r.mnSBTab = nSheetId;
1947 xSupbook->StoreCellRange_(nSheetId, aRange);
1951 std::optional<XclExpSBIndex> XclExpSupbookBuffer::InsertAddIn(const OUString& rName )
1953 XclExpSupbookRef xSupbook;
1954 if( mnAddInSB == SAL_MAX_UINT16 )
1956 xSupbook = new XclExpSupbook( GetRoot() );
1957 mnAddInSB = Append( xSupbook );
1959 else
1960 xSupbook = maSupbookList.GetRecord( mnAddInSB );
1961 OSL_ENSURE( xSupbook, "XclExpSupbookBuffer::InsertAddin - missing add-in supbook" );
1963 sal_uInt16 nExtName = xSupbook->InsertAddIn( rName );
1964 if( nExtName > 0)
1966 return XclExpSBIndex( mnAddInSB, nExtName );
1968 return {};
1971 std::optional<XclExpSBIndex> XclExpSupbookBuffer::InsertEuroTool( const OUString& rName )
1973 XclExpSupbookRef xSupbook;
1974 OUString aUrl( u"\001\010EUROTOOL.XLA"_ustr );
1975 auto nSupbookId = GetSupbookUrl(xSupbook, aUrl);
1976 if ( !nSupbookId )
1978 xSupbook = new XclExpSupbook( GetRoot(), aUrl, XclSupbookType::Eurotool );
1979 nSupbookId = Append( xSupbook );
1982 auto nExtName = xSupbook->InsertEuroTool( rName );
1983 if( nExtName > 0)
1985 return XclExpSBIndex( *nSupbookId, nExtName );
1987 return {};
1990 std::optional<XclExpSBIndex> XclExpSupbookBuffer::InsertDde(
1991 const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
1993 XclExpSupbookRef xSupbook;
1994 auto nSupbook = GetSupbookDde( xSupbook, rApplic, rTopic );
1995 if( !nSupbook )
1997 xSupbook = new XclExpSupbook( GetRoot(), rApplic, rTopic );
1998 nSupbook = Append( xSupbook );
2000 auto nExtName = xSupbook->InsertDde( rItem );
2001 if (nExtName > 0)
2003 return XclExpSBIndex(*nSupbook, nExtName);
2005 return {};
2008 std::optional<XclExpSBIndex> XclExpSupbookBuffer::InsertExtName( const OUString& rUrl,
2009 const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
2011 XclExpSupbookRef xSupbook;
2012 auto nSupbookId = GetSupbookUrl(xSupbook, rUrl);
2013 if ( !nSupbookId )
2015 xSupbook = new XclExpSupbook(GetRoot(), rUrl);
2016 nSupbookId = Append(xSupbook);
2019 auto nExtName = xSupbook->InsertExtName(rName, rArray);
2020 if (nExtName > 0)
2022 return XclExpSBIndex( *nSupbookId, nExtName );
2024 return {};
2027 XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
2028 XclExpRefLogEntry* pRefLogEntry )
2030 XclExpXti aXti(0, EXC_NOTAB, EXC_NOTAB);
2031 ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
2032 const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
2033 if (!pUrl)
2034 return aXti;
2036 XclExpSupbookRef xSupbook;
2037 auto nSupbookId = GetSupbookUrl(xSupbook, *pUrl);
2038 if (!nSupbookId)
2040 xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
2041 nSupbookId = Append(xSupbook);
2043 aXti.mnSupbook = *nSupbookId;
2045 sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
2046 if (nFirstSheetId == EXC_NOTAB)
2048 // first sheet not found in SUPBOOK.
2049 return aXti;
2051 sal_uInt16 nSheetCount = xSupbook->GetTabCount();
2052 for (sal_uInt16 i = 0; i < nXclTabSpan; ++i)
2054 sal_uInt16 nSheetId = nFirstSheetId + i;
2055 if (nSheetId >= nSheetCount)
2056 return aXti;
2058 FindSBIndexEntry f(*nSupbookId, nSheetId);
2059 if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
2061 maSBIndexVec.emplace_back();
2062 XclExpSBIndex& r = maSBIndexVec.back();
2063 r.mnSupbook = *nSupbookId;
2064 r.mnSBTab = nSheetId;
2066 if (i == 0)
2067 aXti.mnFirstSBTab = nSheetId;
2068 if (i == nXclTabSpan - 1)
2069 aXti.mnLastSBTab = nSheetId;
2072 if (pRefLogEntry)
2074 pRefLogEntry->mnFirstXclTab = 0;
2075 pRefLogEntry->mnLastXclTab = 0;
2076 if (xSupbook)
2077 xSupbook->FillRefLogEntry(*pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab);
2080 return aXti;
2083 void XclExpSupbookBuffer::Save( XclExpStream& rStrm )
2085 maSupbookList.Save( rStrm );
2088 void XclExpSupbookBuffer::SaveXml( XclExpXmlStream& rStrm )
2090 // Unused external references are not saved, only kept in memory.
2091 // Those that are saved must be indexed from 1, so indexes must be reordered
2092 ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
2093 vector<sal_uInt16> aExternFileIds;
2094 for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
2096 XclExpSupbookRef xRef(maSupbookList.GetRecord(nPos));
2097 // fileIDs are indexed from 1 in xlsx, and from 0 in ScExternalRefManager
2098 // converting between them require a -1 or +1
2099 if (xRef->GetType() == XclSupbookType::Extern)
2100 aExternFileIds.push_back(xRef->GetFileId() - 1);
2102 if (aExternFileIds.size() > 0)
2103 pRefMgr->setSkipUnusedFileIds(aExternFileIds);
2105 ::std::map< sal_uInt16, OUString > aMap;
2106 for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
2108 XclExpSupbookRef xRef( maSupbookList.GetRecord( nPos));
2109 if (xRef->GetType() != XclSupbookType::Extern)
2110 continue; // handle only external reference (for now?)
2112 sal_uInt16 nId = xRef->GetFileId();
2113 sal_uInt16 nUsedId = pRefMgr->convertFileIdToUsedFileId(nId - 1) + 1;
2114 const OUString& rUrl = xRef->GetUrl();
2115 ::std::pair< ::std::map< sal_uInt16, OUString >::iterator, bool > aInsert(
2116 aMap.insert( ::std::make_pair( nId, rUrl)));
2117 if (!aInsert.second)
2119 SAL_WARN( "sc.filter", "XclExpSupbookBuffer::SaveXml: file ID already used: " << nId <<
2120 " wanted for " << rUrl << " and is " << (*aInsert.first).second <<
2121 (rUrl == (*aInsert.first).second ? " multiple Supbook not supported" : ""));
2122 continue;
2124 OUString sId;
2125 sax_fastparser::FSHelperPtr pExternalLink = rStrm.CreateOutputStream(
2126 XclXmlUtils::GetStreamName( "xl/", "externalLinks/externalLink", nUsedId),
2127 XclXmlUtils::GetStreamName( nullptr, "externalLinks/externalLink", nUsedId),
2128 rStrm.GetCurrentStream()->getOutputStream(),
2129 "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml",
2130 CREATE_OFFICEDOC_RELATION_TYPE("externalLink"),
2131 &sId );
2133 // externalReference entry in workbook externalReferences
2134 rStrm.GetCurrentStream()->singleElement( XML_externalReference,
2135 FSNS(XML_r, XML_id), sId.toUtf8() );
2137 // Each externalBook in a separate stream.
2138 rStrm.PushStream( pExternalLink );
2139 xRef->SaveXml( rStrm );
2140 rStrm.PopStream();
2144 bool XclExpSupbookBuffer::HasExternalReferences() const
2146 for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
2148 if (maSupbookList.GetRecord( nPos)->GetType() == XclSupbookType::Extern)
2149 return true;
2151 return false;
2154 std::optional<sal_uInt16> XclExpSupbookBuffer::GetSupbookUrl(
2155 XclExpSupbookRef& rxSupbook, std::u16string_view rUrl ) const
2157 for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
2159 rxSupbook = maSupbookList.GetRecord( nPos );
2160 if( rxSupbook->IsUrlLink( rUrl ) )
2162 return ulimit_cast< sal_uInt16 >( nPos );
2165 return {};
2168 std::optional<sal_uInt16> XclExpSupbookBuffer::GetSupbookDde( XclExpSupbookRef& rxSupbook,
2169 std::u16string_view rApplic, std::u16string_view rTopic ) const
2171 for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
2173 rxSupbook = maSupbookList.GetRecord( nPos );
2174 if( rxSupbook->IsDdeLink( rApplic, rTopic ) )
2176 return ulimit_cast< sal_uInt16 >( nPos );
2179 return {};
2182 sal_uInt16 XclExpSupbookBuffer::Append( XclExpSupbookRef const & xSupbook )
2184 maSupbookList.AppendRecord( xSupbook );
2185 return ulimit_cast< sal_uInt16 >( maSupbookList.GetSize() - 1 );
2188 // Export link manager ========================================================
2190 XclExpLinkManagerImpl::XclExpLinkManagerImpl( const XclExpRoot& rRoot ) :
2191 XclExpRoot( rRoot )
2195 XclExpLinkManagerImpl5::XclExpLinkManagerImpl5( const XclExpRoot& rRoot ) :
2196 XclExpLinkManagerImpl( rRoot )
2200 void XclExpLinkManagerImpl5::FindExtSheet(
2201 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
2202 SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
2204 FindInternal( rnExtSheet, rnFirstXclTab, nFirstScTab );
2205 if( (rnFirstXclTab == EXC_TAB_DELETED) || (nFirstScTab == nLastScTab) )
2207 rnLastXclTab = rnFirstXclTab;
2209 else
2211 sal_uInt16 nDummyExtSheet;
2212 FindInternal( nDummyExtSheet, rnLastXclTab, nLastScTab );
2215 OSL_ENSURE( !pRefLogEntry, "XclExpLinkManagerImpl5::FindExtSheet - fill reflog entry not implemented" );
2218 sal_uInt16 XclExpLinkManagerImpl5::FindExtSheet( sal_Unicode cCode )
2220 sal_uInt16 nExtSheet;
2221 FindInternal( nExtSheet, cCode );
2222 return nExtSheet;
2225 void XclExpLinkManagerImpl5::FindExtSheet(
2226 sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, sal_uInt16 /*nXclTabSpan*/,
2227 sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnFirstSBTab*/, sal_uInt16& /*rnLastSBTab*/,
2228 XclExpRefLogEntry* /*pRefLogEntry*/ )
2230 // not implemented
2233 void XclExpLinkManagerImpl5::StoreCellRange( const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/, const ScAddress& /*rPos*/ )
2235 // not implemented
2238 void XclExpLinkManagerImpl5::StoreCell( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScAddress& /*rPos*/ )
2240 // not implemented
2243 void XclExpLinkManagerImpl5::StoreCellRange( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScRange& /*rRange*/ )
2245 // not implemented
2248 std::optional<XclExpSBIndex> XclExpLinkManagerImpl5::InsertAddIn( const OUString& rName )
2250 sal_uInt16 nExtSheet, nExtName;
2251 XclExpExtSheetRef xExtSheet = FindInternal( nExtSheet, EXC_EXTSH_ADDIN );
2252 if( xExtSheet )
2254 nExtName = xExtSheet->InsertAddIn( rName );
2255 if(nExtName > 0)
2257 return XclExpSBIndex( nExtSheet, nExtName );
2260 return {};
2263 std::optional<XclExpSBIndex> XclExpLinkManagerImpl5::InsertEuroTool( const OUString& /*rName*/ )
2265 return {};
2268 std::optional<XclExpSBIndex> XclExpLinkManagerImpl5::InsertDde(
2269 const OUString& /*rApplic*/, const OUString& /*rTopic*/, const OUString& /*rItem*/ )
2271 // not implemented
2272 return {};
2275 std::optional<XclExpSBIndex> XclExpLinkManagerImpl5::InsertExtName( const OUString& /*rUrl*/,
2276 const OUString& /*rName*/, const ScExternalRefCache::TokenArrayRef& /*rArray*/ )
2278 // not implemented
2279 return {};
2282 void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm )
2284 if( sal_uInt16 nExtSheetCount = GetExtSheetCount() )
2286 // EXTERNCOUNT record
2287 XclExpUInt16Record( EXC_ID_EXTERNCOUNT, nExtSheetCount ).Save( rStrm );
2288 // list of EXTERNSHEET records with EXTERNNAME, XCT, CRN records
2289 maExtSheetList.Save( rStrm );
2293 void XclExpLinkManagerImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
2295 // not applicable
2298 sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const
2300 return static_cast< sal_uInt16 >( maExtSheetList.GetSize() );
2303 sal_uInt16 XclExpLinkManagerImpl5::AppendInternal( XclExpExtSheetRef const & xExtSheet )
2305 if( GetExtSheetCount() < 0x7FFF )
2307 maExtSheetList.AppendRecord( xExtSheet );
2308 // return negated one-based EXTERNSHEET index (i.e. 0xFFFD for 3rd record)
2309 return static_cast< sal_uInt16 >( -GetExtSheetCount() );
2311 return 0;
2314 void XclExpLinkManagerImpl5::CreateInternal()
2316 if( !maIntTabMap.empty() )
2317 return;
2319 // create EXTERNSHEET records for all internal exported sheets
2320 XclExpTabInfo& rTabInfo = GetTabInfo();
2321 for( SCTAB nScTab = 0, nScCnt = rTabInfo.GetScTabCount(); nScTab < nScCnt; ++nScTab )
2323 if( rTabInfo.IsExportTab( nScTab ) )
2325 XclExpExtSheetRef xRec;
2326 if( nScTab == GetCurrScTab() )
2327 xRec = new XclExpExternSheet( GetRoot(), EXC_EXTSH_OWNTAB );
2328 else
2329 xRec = new XclExpExternSheet( GetRoot(), rTabInfo.GetScTabName( nScTab ) );
2330 maIntTabMap[ nScTab ] = AppendInternal( xRec );
2335 XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::GetInternal( sal_uInt16 nExtSheet )
2337 return maExtSheetList.GetRecord( static_cast< sal_uInt16 >( -nExtSheet - 1 ) );
2340 XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
2341 sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab )
2343 // create internal EXTERNSHEET records on demand
2344 CreateInternal();
2346 // try to find an EXTERNSHEET record - if not, return a "deleted sheet" reference
2347 XclExpExtSheetRef xExtSheet;
2348 XclExpIntTabMap::const_iterator aIt = maIntTabMap.find( nScTab );
2349 if( aIt == maIntTabMap.end() )
2351 xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_OWNDOC );
2352 rnXclTab = EXC_TAB_DELETED;
2354 else
2356 rnExtSheet = aIt->second;
2357 xExtSheet = GetInternal( rnExtSheet );
2358 rnXclTab = GetTabInfo().GetXclTab( nScTab );
2360 return xExtSheet;
2363 XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
2364 sal_uInt16& rnExtSheet, sal_Unicode cCode )
2366 XclExpExtSheetRef xExtSheet;
2367 XclExpCodeMap::const_iterator aIt = maCodeMap.find( cCode );
2368 if( aIt == maCodeMap.end() )
2370 xExtSheet = new XclExpExternSheet( GetRoot(), cCode );
2371 rnExtSheet = maCodeMap[ cCode ] = AppendInternal( xExtSheet );
2373 else
2375 rnExtSheet = aIt->second;
2376 xExtSheet = GetInternal( rnExtSheet );
2378 return xExtSheet;
2381 XclExpLinkManagerImpl8::XclExpLinkManagerImpl8( const XclExpRoot& rRoot ) :
2382 XclExpLinkManagerImpl( rRoot ),
2383 maSBBuffer( rRoot )
2387 void XclExpLinkManagerImpl8::FindExtSheet(
2388 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
2389 SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
2391 XclExpTabInfo& rTabInfo = GetTabInfo();
2392 rnFirstXclTab = rTabInfo.GetXclTab( nFirstScTab );
2393 rnLastXclTab = rTabInfo.GetXclTab( nLastScTab );
2394 rnExtSheet = InsertXti( maSBBuffer.GetXti( rnFirstXclTab, rnLastXclTab, pRefLogEntry ) );
2397 sal_uInt16 XclExpLinkManagerImpl8::FindExtSheet( sal_Unicode cCode )
2399 OSL_ENSURE( (cCode == EXC_EXTSH_OWNDOC) || (cCode == EXC_EXTSH_ADDIN),
2400 "XclExpLinkManagerImpl8::FindExtSheet - unknown externsheet code" );
2401 return InsertXti( maSBBuffer.GetXti( EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
2404 void XclExpLinkManagerImpl8::FindExtSheet(
2405 sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
2406 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
2407 XclExpRefLogEntry* pRefLogEntry )
2409 XclExpXti aXti = maSBBuffer.GetXti(nFileId, rTabName, nXclTabSpan, pRefLogEntry);
2410 rnExtSheet = InsertXti(aXti);
2411 rnFirstSBTab = aXti.mnFirstSBTab;
2412 rnLastSBTab = aXti.mnLastSBTab;
2415 void XclExpLinkManagerImpl8::StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos )
2417 ScAddress aAbs1 = rRef1.toAbs(GetRoot().GetDoc(), rPos);
2418 ScAddress aAbs2 = rRef2.toAbs(GetRoot().GetDoc(), rPos);
2419 if (!(!rRef1.IsDeleted() && !rRef2.IsDeleted() && (aAbs1.Tab() >= 0) && (aAbs2.Tab() >= 0)))
2420 return;
2422 const XclExpTabInfo& rTabInfo = GetTabInfo();
2423 SCTAB nFirstScTab = aAbs1.Tab();
2424 SCTAB nLastScTab = aAbs2.Tab();
2425 ScRange aRange(aAbs1.Col(), aAbs1.Row(), 0, aAbs2.Col(), aAbs2.Row(), 0);
2426 for (SCTAB nScTab = nFirstScTab; nScTab <= nLastScTab; ++nScTab)
2428 if( rTabInfo.IsExternalTab( nScTab ) )
2430 aRange.aStart.SetTab( nScTab );
2431 aRange.aEnd.SetTab( nScTab );
2432 maSBBuffer.StoreCellRange( aRange );
2437 void XclExpLinkManagerImpl8::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
2439 maSBBuffer.StoreCell(nFileId, rTabName, rPos);
2442 void XclExpLinkManagerImpl8::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
2444 maSBBuffer.StoreCellRange(nFileId, rTabName, rRange);
2447 std::optional<XclExpSBIndex> XclExpLinkManagerImpl8::InsertAddIn( const OUString& rName )
2449 const auto pResult = maSBBuffer.InsertAddIn( rName );
2450 if( pResult )
2452 return XclExpSBIndex(InsertXti( XclExpXti( pResult->mnSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ), pResult->mnSBTab);
2454 return {};
2457 std::optional<XclExpSBIndex> XclExpLinkManagerImpl8::InsertEuroTool( const OUString& rName )
2459 const auto pResult = maSBBuffer.InsertEuroTool( rName );
2460 if( pResult )
2462 return XclExpSBIndex(InsertXti( XclExpXti( pResult->mnSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ),
2463 pResult->mnSBTab);
2465 return {};
2468 std::optional<XclExpSBIndex> XclExpLinkManagerImpl8::InsertDde(
2469 const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
2471 const auto pResult = maSBBuffer.InsertDde( rApplic, rTopic, rItem );
2472 if( pResult )
2474 return XclExpSBIndex(InsertXti( XclExpXti( pResult->mnSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ),
2475 pResult->mnSBTab);
2477 return {};
2480 std::optional<XclExpSBIndex> XclExpLinkManagerImpl8::InsertExtName( const OUString& rUrl, const OUString& rName,
2481 const ScExternalRefCache::TokenArrayRef& rArray )
2483 const auto pResult = maSBBuffer.InsertExtName( rUrl, rName, rArray );
2484 if( pResult )
2486 return XclExpSBIndex(InsertXti( XclExpXti( pResult->mnSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) ),
2487 pResult->mnSBTab);
2489 return {};
2492 void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm )
2494 if( maXtiVec.empty() )
2495 return;
2497 // SUPBOOKs, XCTs, CRNs, EXTERNNAMEs
2498 maSBBuffer.Save( rStrm );
2500 // EXTERNSHEET
2501 sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXtiVec.size() );
2502 rStrm.StartRecord( EXC_ID_EXTERNSHEET, 2 + 6 * nCount );
2503 rStrm << nCount;
2504 rStrm.SetSliceSize( 6 );
2505 for( const auto& rXti : maXtiVec )
2506 rXti.Save( rStrm );
2507 rStrm.EndRecord();
2510 void XclExpLinkManagerImpl8::SaveXml( XclExpXmlStream& rStrm )
2512 if (maSBBuffer.HasExternalReferences())
2514 sax_fastparser::FSHelperPtr pWorkbook = rStrm.GetCurrentStream();
2515 pWorkbook->startElement(XML_externalReferences);
2517 // externalLink, externalBook, sheetNames, sheetDataSet, externalName
2518 maSBBuffer.SaveXml( rStrm );
2520 pWorkbook->endElement( XML_externalReferences);
2523 // TODO: equivalent for EXTERNSHEET in OOXML?
2524 #if 0
2525 if( !maXtiVec.empty() )
2527 for( const auto& rXti : maXtiVec )
2528 rXti.SaveXml( rStrm );
2530 #endif
2533 sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti )
2535 auto aIt = std::find(maXtiVec.begin(), maXtiVec.end(), rXti);
2536 if (aIt != maXtiVec.end())
2537 return ulimit_cast< sal_uInt16 >( std::distance(maXtiVec.begin(), aIt) );
2538 maXtiVec.push_back( rXti );
2539 return ulimit_cast< sal_uInt16 >( maXtiVec.size() - 1 );
2542 XclExpLinkManager::XclExpLinkManager( const XclExpRoot& rRoot ) :
2543 XclExpRoot( rRoot )
2545 switch( GetBiff() )
2547 case EXC_BIFF5:
2548 mxImpl = std::make_shared<XclExpLinkManagerImpl5>( rRoot );
2549 break;
2550 case EXC_BIFF8:
2551 mxImpl = std::make_shared<XclExpLinkManagerImpl8>( rRoot );
2552 break;
2553 default:
2554 DBG_ERROR_BIFF();
2558 XclExpLinkManager::~XclExpLinkManager()
2562 void XclExpLinkManager::FindExtSheet(
2563 sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab,
2564 SCTAB nScTab, XclExpRefLogEntry* pRefLogEntry )
2566 mxImpl->FindExtSheet( rnExtSheet, rnXclTab, rnXclTab, nScTab, nScTab, pRefLogEntry );
2569 void XclExpLinkManager::FindExtSheet(
2570 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
2571 SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
2573 mxImpl->FindExtSheet( rnExtSheet, rnFirstXclTab, rnLastXclTab, nFirstScTab, nLastScTab, pRefLogEntry );
2576 sal_uInt16 XclExpLinkManager::FindExtSheet( sal_Unicode cCode )
2578 return mxImpl->FindExtSheet( cCode );
2581 void XclExpLinkManager::FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
2582 sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
2583 XclExpRefLogEntry* pRefLogEntry )
2585 mxImpl->FindExtSheet( nFileId, rTabName, nXclTabSpan, rnExtSheet, rnFirstSBTab, rnLastSBTab, pRefLogEntry );
2588 void XclExpLinkManager::StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos )
2590 mxImpl->StoreCellRange(rRef, rRef, rPos);
2593 void XclExpLinkManager::StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos )
2595 mxImpl->StoreCellRange(rRef.Ref1, rRef.Ref2, rPos);
2598 void XclExpLinkManager::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
2600 mxImpl->StoreCell(nFileId, rTabName, rPos);
2603 void XclExpLinkManager::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
2605 mxImpl->StoreCellRange(nFileId, rTabName, rRange);
2608 std::optional<XclExpSBIndex> XclExpLinkManager::InsertAddIn( const OUString& rName )
2610 return mxImpl->InsertAddIn( rName );
2613 std::optional<XclExpSBIndex> XclExpLinkManager::InsertEuroTool( const OUString& rName )
2615 return mxImpl->InsertEuroTool( rName );
2618 std::optional<XclExpSBIndex> XclExpLinkManager::InsertDde(
2619 const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
2621 return mxImpl->InsertDde( rApplic, rTopic, rItem );
2624 std::optional<XclExpSBIndex> XclExpLinkManager::InsertExtName( const OUString& rUrl, const OUString& rName,
2625 const ScExternalRefCache::TokenArrayRef& rArray )
2627 return mxImpl->InsertExtName( rUrl, rName, rArray);
2630 void XclExpLinkManager::Save( XclExpStream& rStrm )
2632 mxImpl->Save( rStrm );
2635 void XclExpLinkManager::SaveXml( XclExpXmlStream& rStrm )
2637 mxImpl->SaveXml( rStrm );
2640 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */