Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xename.cxx
blob1e84fa0e0711669c6dce36e66e110264876400a0
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 <xename.hxx>
22 #include <map>
24 #include <document.hxx>
25 #include <rangenam.hxx>
26 #include <tokenarray.hxx>
27 #include <xehelper.hxx>
28 #include <xelink.hxx>
29 #include <excrecds.hxx>
30 #include <xlname.hxx>
31 #include <xeformula.hxx>
32 #include <xestring.hxx>
33 #include <xltools.hxx>
35 #include <formula/grammar.hxx>
36 #include <oox/export/utils.hxx>
37 #include <oox/token/tokens.hxx>
39 using namespace ::oox;
41 // *** Helper classes ***
43 /** Represents an internal defined name, supports writing it to a NAME record. */
44 class XclExpName : public XclExpRecord, protected XclExpRoot
46 public:
47 /** Creates a standard defined name. */
48 explicit XclExpName( const XclExpRoot& rRoot, const OUString& rName );
49 /** Creates a built-in defined name. */
50 explicit XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn );
52 /** Sets a token array containing the definition of this name. */
53 void SetTokenArray( const XclTokenArrayRef& xTokArr );
54 /** Changes this defined name to be local on the specified Calc sheet. */
55 void SetLocalTab( SCTAB nScTab );
56 /** Hides or unhides the defined name. */
57 void SetHidden( bool bHidden = true );
58 /** Changes this name to be the call to a VB macro function or procedure.
59 @param bVBasic true = Visual Basic macro, false = Sheet macro.
60 @param bFunc true = Macro function; false = Macro procedure. */
61 void SetMacroCall( bool bVBasic, bool bFunc );
63 /** Sets the name's symbol value
64 @param sValue the name's symbolic value */
65 void SetSymbol( const OUString& rValue );
67 /** Returns the original name (title) of this defined name. */
68 const OUString& GetOrigName() const { return maOrigName; }
69 /** Returns the Excel built-in name index of this defined name.
70 @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */
71 sal_Unicode GetBuiltInName() const { return mcBuiltIn; }
73 /** Returns the symbol value for this defined name. */
74 const OUString& GetSymbol() const { return msSymbol; }
76 /** Returns true, if this is a document-global defined name. */
77 bool IsGlobal() const { return mnXclTab == EXC_NAME_GLOBAL; }
78 /** Returns the Calc sheet of a local defined name. */
79 SCTAB GetScTab() const { return mnScTab; }
81 /** Returns true, if this defined name is volatile. */
82 bool IsVolatile() const;
83 /** Returns true, if this defined name describes a macro call.
84 @param bFunc true = Macro function; false = Macro procedure. */
85 bool IsMacroCall( bool bVBasic, bool bFunc ) const;
87 /** Writes the entire NAME record to the passed stream. */
88 virtual void Save( XclExpStream& rStrm ) override;
90 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
92 private:
93 /** Writes the body of the NAME record to the passed stream. */
94 virtual void WriteBody( XclExpStream& rStrm ) override;
95 /** Convert localized range separators */
96 OUString GetWithDefaultRangeSeparator( const OUString& rSymbol ) const;
98 private:
99 OUString maOrigName; /// The original user-defined name.
100 OUString msSymbol; /// The value of the symbol
101 XclExpStringRef mxName; /// The name as Excel string object.
102 XclTokenArrayRef mxTokArr; /// The definition of the defined name.
103 sal_Unicode mcBuiltIn; /// The built-in index for built-in names.
104 SCTAB mnScTab; /// The Calc sheet index for local names.
105 sal_uInt16 mnFlags; /// Additional flags for this defined name.
106 sal_uInt16 mnExtSheet; /// The 1-based index to a global EXTERNSHEET record.
107 sal_uInt16 mnXclTab; /// The 1-based Excel sheet index for local names.
110 /** Implementation class of the name manager. */
111 class XclExpNameManagerImpl : protected XclExpRoot
113 public:
114 explicit XclExpNameManagerImpl( const XclExpRoot& rRoot );
116 /** Creates NAME records for built-in and user defined names. */
117 void Initialize();
119 /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
120 sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
122 /** Inserts a new built-in defined name. */
123 sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& aRangeList );
124 sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange );
125 /** Inserts a new defined name. Sets another unused name, if rName already exists. */
126 sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
127 /** Returns index of an existing name, or creates a name without definition. */
128 sal_uInt16 InsertRawName( const OUString& rName );
129 /** Searches or inserts a defined name describing a macro name.
130 @param bVBasic true = Visual Basic macro; false = Sheet macro.
131 @param bFunc true = Macro function; false = Macro procedure. */
132 sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden );
134 /** Returns the NAME record at the specified position or 0 on error. */
135 const XclExpName* GetName( sal_uInt16 nNameIdx ) const;
137 /** Writes the entire list of NAME records.
138 @descr In BIFF7 and lower, writes the entire global link table, which
139 consists of an EXTERNCOUNT record, several EXTERNSHEET records, and
140 the list of NAME records. */
141 void Save( XclExpStream& rStrm );
143 void SaveXml( XclExpXmlStream& rStrm );
145 private:
146 typedef XclExpRecordList< XclExpName > XclExpNameList;
147 typedef XclExpNameList::RecordRefType XclExpNameRef;
149 typedef ::std::map< ::std::pair<SCTAB, OUString>, sal_uInt16> NamedExpMap;
151 private:
153 * @param nTab 0-based table index, or SCTAB_GLOBAL for global names.
154 * @param nScIdx calc's name index.
156 * @return excel's name index.
158 sal_uInt16 FindNamedExp( SCTAB nTab, OUString sName );
160 /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */
161 sal_uInt16 FindBuiltInNameIdx( const OUString& rName,
162 const OUString& sSymbol ) const;
163 /** Returns an unused name for the passed name. */
164 OUString GetUnusedName( const OUString& rName ) const;
166 /** Appends a new NAME record to the record list.
167 @return The 1-based NAME record index used elsewhere in the Excel file. */
168 sal_uInt16 Append( const XclExpNameRef& xName );
169 /** Creates a new NAME record for the passed user-defined name.
170 @return The 1-based NAME record index used elsewhere in the Excel file. */
171 sal_uInt16 CreateName( SCTAB nTab, const ScRangeData& rRangeData );
173 /** Creates NAME records for all built-in names in the document. */
174 void CreateBuiltInNames();
175 /** Creates NAME records for all user-defined names in the document. */
176 void CreateUserNames();
178 private:
180 * Maps Calc's named range to Excel's NAME records. Global names use
181 * -1 as their table index, whereas sheet-local names have 0-based table
182 * index.
184 NamedExpMap maNamedExpMap;
185 XclExpNameList maNameList; /// List of NAME records.
186 size_t mnFirstUserIdx; /// List index of first user-defined NAME record.
189 // *** Implementation ***
191 XclExpName::XclExpName( const XclExpRoot& rRoot, const OUString& rName ) :
192 XclExpRecord( EXC_ID_NAME ),
193 XclExpRoot( rRoot ),
194 maOrigName( rName ),
195 mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
196 mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
197 mnScTab( SCTAB_GLOBAL ),
198 mnFlags( EXC_NAME_DEFAULT ),
199 mnExtSheet( EXC_NAME_GLOBAL ),
200 mnXclTab( EXC_NAME_GLOBAL )
204 XclExpName::XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ) :
205 XclExpRecord( EXC_ID_NAME ),
206 XclExpRoot( rRoot ),
207 mcBuiltIn( cBuiltIn ),
208 mnScTab( SCTAB_GLOBAL ),
209 mnFlags( EXC_NAME_DEFAULT ),
210 mnExtSheet( EXC_NAME_GLOBAL ),
211 mnXclTab( EXC_NAME_GLOBAL )
213 // filter source range is hidden in Excel
214 if( cBuiltIn == EXC_BUILTIN_FILTERDATABASE )
215 SetHidden();
217 // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag
218 if( (GetBiff() <= EXC_BIFF5) && (cBuiltIn == EXC_BUILTIN_FILTERDATABASE) )
220 OUString aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE ) );
221 mxName = XclExpStringHelper::CreateString( rRoot, aName, XclStrFlags::EightBitLength );
222 maOrigName = XclTools::GetXclBuiltInDefName( cBuiltIn );
224 else
226 maOrigName = XclTools::GetBuiltInDefNameXml( cBuiltIn ) ;
227 mxName = XclExpStringHelper::CreateString( rRoot, cBuiltIn, XclStrFlags::EightBitLength );
228 ::set_flag( mnFlags, EXC_NAME_BUILTIN );
232 void XclExpName::SetTokenArray( const XclTokenArrayRef& xTokArr )
234 mxTokArr = xTokArr;
237 void XclExpName::SetLocalTab( SCTAB nScTab )
239 OSL_ENSURE( GetTabInfo().IsExportTab( nScTab ), "XclExpName::SetLocalTab - invalid sheet index" );
240 if( GetTabInfo().IsExportTab( nScTab ) )
242 mnScTab = nScTab;
243 GetGlobalLinkManager().FindExtSheet( mnExtSheet, mnXclTab, nScTab );
245 // special handling for NAME record
246 switch( GetBiff() )
248 case EXC_BIFF5: // EXTERNSHEET index is positive in NAME record
249 mnExtSheet = ~mnExtSheet + 1;
250 break;
251 case EXC_BIFF8: // EXTERNSHEET index not used, but must be created in link table
252 mnExtSheet = 0;
253 break;
254 default: DBG_ERROR_BIFF();
257 // Excel sheet index is 1-based
258 ++mnXclTab;
262 void XclExpName::SetHidden( bool bHidden )
264 ::set_flag( mnFlags, EXC_NAME_HIDDEN, bHidden );
267 void XclExpName::SetMacroCall( bool bVBasic, bool bFunc )
269 ::set_flag( mnFlags, EXC_NAME_PROC );
270 ::set_flag( mnFlags, EXC_NAME_VB, bVBasic );
271 ::set_flag( mnFlags, EXC_NAME_FUNC, bFunc );
274 void XclExpName::SetSymbol( const OUString& rSymbol )
276 msSymbol = rSymbol;
279 bool XclExpName::IsVolatile() const
281 return mxTokArr && mxTokArr->IsVolatile();
284 bool XclExpName::IsMacroCall( bool bVBasic, bool bFunc ) const
286 return
287 (::get_flag( mnFlags, EXC_NAME_VB ) == bVBasic) &&
288 (::get_flag( mnFlags, EXC_NAME_FUNC ) == bFunc);
291 void XclExpName::Save( XclExpStream& rStrm )
293 OSL_ENSURE( mxName && (mxName->Len() > 0), "XclExpName::Save - missing name" );
294 OSL_ENSURE( !(IsGlobal() && ::get_flag( mnFlags, EXC_NAME_BUILTIN )), "XclExpName::Save - global built-in name" );
295 SetRecSize( 11 + mxName->GetSize() + (mxTokArr ? mxTokArr->GetSize() : 2) );
296 XclExpRecord::Save( rStrm );
299 OUString XclExpName::GetWithDefaultRangeSeparator( const OUString& rSymbol ) const
301 sal_Int32 nPos = rSymbol.indexOf(';');
302 if ( nPos > -1 )
304 // convert with validation
305 ScRange aRange;
306 ScAddress::Details detailsXL( ::formula::FormulaGrammar::CONV_XL_A1 );
307 ScRefFlags nRes = aRange.Parse( rSymbol.copy(0, nPos), &GetDoc(), detailsXL );
308 if ( nRes & ScRefFlags::VALID )
310 nRes = aRange.Parse( rSymbol.copy(nPos+1), &GetDoc(), detailsXL );
311 if ( nRes & ScRefFlags::VALID )
313 return rSymbol.replaceFirst(";", ",");
317 return rSymbol;
320 void XclExpName::SaveXml( XclExpXmlStream& rStrm )
322 sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
323 rWorkbook->startElement( XML_definedName,
324 // OOXTODO: XML_comment, "",
325 // OOXTODO: XML_customMenu, "",
326 // OOXTODO: XML_description, "",
327 XML_function, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ),
328 // OOXTODO: XML_functionGroupId, "",
329 // OOXTODO: XML_help, "",
330 XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_NAME_HIDDEN ) ),
331 XML_localSheetId, mnScTab == SCTAB_GLOBAL ? nullptr : OString::number( mnScTab ).getStr(),
332 XML_name, maOrigName.toUtf8(),
333 // OOXTODO: XML_publishToServer, "",
334 // OOXTODO: XML_shortcutKey, "",
335 // OOXTODO: XML_statusBar, "",
336 XML_vbProcedure, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) )
337 // OOXTODO: XML_workbookParameter, "",
338 // OOXTODO: XML_xlm, ""
340 rWorkbook->writeEscaped( GetWithDefaultRangeSeparator( msSymbol ) );
341 rWorkbook->endElement( XML_definedName );
344 void XclExpName::WriteBody( XclExpStream& rStrm )
346 sal_uInt16 nFmlaSize = mxTokArr ? mxTokArr->GetSize() : 0;
348 rStrm << mnFlags // flags
349 << sal_uInt8( 0 ); // keyboard shortcut
350 mxName->WriteLenField( rStrm ); // length of name
351 rStrm << nFmlaSize // size of token array
352 << mnExtSheet // BIFF5/7: EXTSHEET index, BIFF8: not used
353 << mnXclTab // 1-based sheet index for local names
354 << sal_uInt32( 0 ); // length of menu/descr/help/status text
355 mxName->WriteFlagField( rStrm ); // BIFF8 flag field (no-op in <=BIFF7)
356 mxName->WriteBuffer( rStrm ); // character array of the name
357 if( mxTokArr )
358 mxTokArr->WriteArray( rStrm ); // token array without size
361 /** Returns true (needed fixing) if FormulaToken was not absolute and 3D.
362 So, regardless of whether the fix was successful or not, true is still returned since a fix was required.*/
363 static bool lcl_EnsureAbs3DToken( const SCTAB nTab, formula::FormulaToken* pTok, const bool bFix = true )
365 bool bFixRequired = false;
366 if ( !pTok || ( pTok->GetType() != formula::svSingleRef && pTok->GetType() != formula::svDoubleRef ) )
367 return bFixRequired;
369 ScSingleRefData* pRef1 = pTok->GetSingleRef();
370 if ( !pRef1 )
371 return bFixRequired;
373 ScSingleRefData* pRef2 = nullptr;
374 if ( pTok->GetType() == formula::svDoubleRef )
375 pRef2 = pTok->GetSingleRef2();
377 if ( pRef1->IsTabRel() || !pRef1->IsFlag3D() )
379 bFixRequired = true;
380 if ( bFix )
382 if ( pRef1->IsTabRel() && nTab != SCTAB_GLOBAL )
383 pRef1->SetAbsTab( nTab + pRef1->Tab() ); //XLS requirement
384 if ( !pRef1->IsTabRel() )
386 pRef1->SetFlag3D( true ); //XLSX requirement
387 if ( pRef2 && !pRef2->IsTabRel() )
388 pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
393 if ( pRef2 && pRef2->IsTabRel() && !pRef1->IsTabRel() )
395 bFixRequired = true;
396 if ( bFix && nTab != SCTAB_GLOBAL )
398 pRef2->SetAbsTab( nTab + pRef2->Tab() );
399 pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
402 return bFixRequired;
405 XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot& rRoot ) :
406 XclExpRoot( rRoot ),
407 mnFirstUserIdx( 0 )
411 void XclExpNameManagerImpl::Initialize()
413 CreateBuiltInNames();
414 mnFirstUserIdx = maNameList.GetSize();
415 CreateUserNames();
418 sal_uInt16 XclExpNameManagerImpl::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
420 sal_uInt16 nNameIdx = 0;
421 const ScRangeData* pData = nullptr;
422 ScRangeName* pRN = (nTab == SCTAB_GLOBAL) ? GetDoc().GetRangeName() : GetDoc().GetRangeName(nTab);
423 if (pRN)
424 pData = pRN->findByIndex(nScNameIdx);
426 if (pData)
428 bool bEmulateGlobalRelativeTable = false;
429 const ScTokenArray* pCode = pData->GetCode();
430 if ( pCode
431 && nTab == SCTAB_GLOBAL
432 && (pData->HasType( ScRangeData::Type::AbsPos ) || pData->HasType( ScRangeData::Type::AbsArea )) )
434 bEmulateGlobalRelativeTable = lcl_EnsureAbs3DToken( nTab, pCode->FirstToken(), /*bFix=*/false );
436 nNameIdx = FindNamedExp( bEmulateGlobalRelativeTable ? nCurrTab : nTab, pData->GetName() );
437 if (!nNameIdx)
438 nNameIdx = CreateName(nTab, *pData);
441 return nNameIdx;
444 sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange )
446 XclExpNameRef xName( new XclExpName( GetRoot(), cBuiltIn ) );
447 xName->SetTokenArray( xTokArr );
448 xName->SetLocalTab( aRange.aStart.Tab() );
449 OUString sSymbol(aRange.Format(ScRefFlags::RANGE_ABS_3D, &GetDoc(), ScAddress::Details( ::formula::FormulaGrammar::CONV_XL_A1)));
450 xName->SetSymbol( sSymbol );
451 return Append( xName );
454 sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& rRangeList )
456 XclExpNameRef xName( new XclExpName( GetRoot(), cBuiltIn ) );
457 xName->SetTokenArray( xTokArr );
458 xName->SetLocalTab( nScTab );
459 OUString sSymbol;
460 rRangeList.Format( sSymbol, ScRefFlags::RANGE_ABS_3D, &GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1 );
461 xName->SetSymbol( sSymbol );
462 return Append( xName );
465 sal_uInt16 XclExpNameManagerImpl::InsertUniqueName(
466 const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
468 OSL_ENSURE( !rName.isEmpty(), "XclExpNameManagerImpl::InsertUniqueName - empty name" );
469 XclExpNameRef xName( new XclExpName( GetRoot(), GetUnusedName( rName ) ) );
470 xName->SetTokenArray( xTokArr );
471 xName->SetLocalTab( nScTab );
472 return Append( xName );
475 sal_uInt16 XclExpNameManagerImpl::InsertRawName( const OUString& rName )
477 // empty name? may occur in broken external Calc tokens
478 if( rName.isEmpty() )
479 return 0;
481 // try to find an existing NAME record, regardless of its type
482 for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
484 XclExpNameRef xName = maNameList.GetRecord( nListIdx );
485 if( xName->IsGlobal() && (xName->GetOrigName() == rName) )
486 return static_cast< sal_uInt16 >( nListIdx + 1 );
489 // create a new NAME record
490 XclExpNameRef xName( new XclExpName( GetRoot(), rName ) );
491 return Append( xName );
494 sal_uInt16 XclExpNameManagerImpl::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
496 // empty name? may occur in broken external Calc tokens
497 if( rMacroName.isEmpty() )
498 return 0;
500 // try to find an existing NAME record
501 for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
503 XclExpNameRef xName = maNameList.GetRecord( nListIdx );
504 if( xName->IsMacroCall( bVBasic, bFunc ) && (xName->GetOrigName() == rMacroName) )
505 return static_cast< sal_uInt16 >( nListIdx + 1 );
508 // create a new NAME record
509 XclExpNameRef xName( new XclExpName( GetRoot(), rMacroName ) );
510 xName->SetMacroCall( bVBasic, bFunc );
511 xName->SetHidden( bHidden );
513 // for sheet macros, add a #NAME! error
514 if( !bVBasic )
515 xName->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME ) );
517 return Append( xName );
520 const XclExpName* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx ) const
522 OSL_ENSURE( maNameList.HasRecord( nNameIdx - 1 ), "XclExpNameManagerImpl::GetName - wrong record index" );
523 return maNameList.GetRecord( nNameIdx - 1 ).get();
526 void XclExpNameManagerImpl::Save( XclExpStream& rStrm )
528 maNameList.Save( rStrm );
531 void XclExpNameManagerImpl::SaveXml( XclExpXmlStream& rStrm )
533 if( maNameList.IsEmpty() )
534 return;
535 sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
536 rWorkbook->startElement(XML_definedNames);
537 maNameList.SaveXml( rStrm );
538 rWorkbook->endElement( XML_definedNames );
541 // private --------------------------------------------------------------------
543 sal_uInt16 XclExpNameManagerImpl::FindNamedExp( SCTAB nTab, OUString sName )
545 NamedExpMap::key_type key(nTab, sName);
546 NamedExpMap::const_iterator itr = maNamedExpMap.find(key);
547 return (itr == maNamedExpMap.end()) ? 0 : itr->second;
550 sal_uInt16 XclExpNameManagerImpl::FindBuiltInNameIdx(
551 const OUString& rName, const OUString& sSymbol ) const
553 /* Get built-in index from the name. Special case: the database range
554 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */
555 sal_Unicode cBuiltIn = XclTools::GetBuiltInDefNameIndex( rName );
557 if( cBuiltIn < EXC_BUILTIN_UNKNOWN )
559 // try to find the record in existing built-in NAME record list
560 for( size_t nPos = 0; nPos < mnFirstUserIdx; ++nPos )
562 XclExpNameRef xName = maNameList.GetRecord( nPos );
563 if( xName->GetBuiltInName() == cBuiltIn && xName->GetSymbol().replace(';', ',') == sSymbol.replace(';', ',') )
565 // tdf#112567 restore the original built-in names with non-localized separators
566 // TODO: support more localizations, if needed
567 if ( xName->GetSymbol() != sSymbol )
569 xName->SetSymbol(xName->GetSymbol().replace(';', ','));
571 return static_cast< sal_uInt16 >( nPos + 1 );
575 return 0;
578 OUString XclExpNameManagerImpl::GetUnusedName( const OUString& rName ) const
580 OUString aNewName( rName );
581 sal_Int32 nAppIdx = 0;
582 bool bExist = true;
583 while( bExist )
585 // search the list of user-defined names
586 bExist = false;
587 for( size_t nPos = mnFirstUserIdx, nSize = maNameList.GetSize(); !bExist && (nPos < nSize); ++nPos )
589 XclExpNameRef xName = maNameList.GetRecord( nPos );
590 bExist = xName->GetOrigName() == aNewName;
591 // name exists -> create a new name "<originalname>_<counter>"
592 if( bExist )
593 aNewName = rName + "_" + OUString::number( ++nAppIdx );
596 return aNewName;
599 sal_uInt16 XclExpNameManagerImpl::Append( const XclExpNameRef& xName )
601 if( maNameList.GetSize() == 0xFFFF )
602 return 0;
603 maNameList.AppendRecord( xName );
604 return static_cast< sal_uInt16 >( maNameList.GetSize() ); // 1-based
607 sal_uInt16 XclExpNameManagerImpl::CreateName( SCTAB nTab, const ScRangeData& rRangeData )
609 const OUString& rName = rRangeData.GetName();
611 /* #i38821# recursive names: first insert the (empty) name object,
612 otherwise a recursive call of this function from the formula compiler
613 with the same defined name will not find it and will create it again. */
614 size_t nOldListSize = maNameList.GetSize();
615 XclExpNameRef xName( new XclExpName( GetRoot(), rName ) );
616 if (nTab != SCTAB_GLOBAL)
617 xName->SetLocalTab(nTab);
618 sal_uInt16 nNameIdx = Append( xName );
619 // store the index of the NAME record in the lookup map
620 NamedExpMap::key_type key(nTab, rRangeData.GetName());
621 maNamedExpMap[key] = nNameIdx;
623 /* Create the definition formula.
624 This may cause recursive creation of other defined names. */
625 if( const ScTokenArray* pScTokArr = const_cast< ScRangeData& >( rRangeData ).GetCode() )
627 XclTokenArrayRef xTokArr;
628 OUString sSymbol;
629 // MSO requires named ranges to have absolute sheet references
630 if ( rRangeData.HasType( ScRangeData::Type::AbsPos ) || rRangeData.HasType( ScRangeData::Type::AbsArea ) )
632 // Don't modify the actual document; use a temporary copy to create the export formulas.
633 std::unique_ptr<ScTokenArray> pTokenCopy( pScTokArr->Clone() );
634 lcl_EnsureAbs3DToken(nTab, pTokenCopy->FirstToken());
636 xTokArr = GetFormulaCompiler().CreateFormula(EXC_FMLATYPE_NAME, *pTokenCopy);
637 if ( GetOutput() != EXC_OUTPUT_BINARY )
639 ScCompiler aComp(&GetDoc(), rRangeData.GetPos(), *pTokenCopy,
640 formula::FormulaGrammar::GRAM_OOXML);
641 aComp.CreateStringFromTokenArray( sSymbol );
644 else
646 xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, *pScTokArr );
647 rRangeData.GetSymbol( sSymbol, ((GetOutput() == EXC_OUTPUT_BINARY) ?
648 formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 : formula::FormulaGrammar::GRAM_OOXML));
650 xName->SetTokenArray( xTokArr );
651 xName->SetSymbol( sSymbol );
653 /* Try to replace by existing built-in name - complete token array is
654 needed for comparison, and due to the recursion problem above this
655 cannot be done earlier. If a built-in name is found, the created NAME
656 record for this name and all following records in the list must be
657 deleted, otherwise they may contain wrong name list indexes. */
658 sal_uInt16 nBuiltInIdx = FindBuiltInNameIdx( rName, sSymbol );
659 if( nBuiltInIdx != 0 )
661 // delete the new NAME records
662 while( maNameList.GetSize() > nOldListSize )
663 maNameList.RemoveRecord( maNameList.GetSize() - 1 );
664 // use index of the found built-in NAME record
665 key = NamedExpMap::key_type(nTab, rRangeData.GetName());
666 maNamedExpMap[key] = nNameIdx = nBuiltInIdx;
670 return nNameIdx;
673 void XclExpNameManagerImpl::CreateBuiltInNames()
675 ScDocument& rDoc = GetDoc();
676 XclExpTabInfo& rTabInfo = GetTabInfo();
678 /* #i2394# built-in defined names must be sorted by the name of the
679 containing sheet. Example: SheetA!Print_Range must be stored *before*
680 SheetB!Print_Range, regardless of the position of SheetA in the document! */
681 for( SCTAB nScTabIdx = 0, nScTabCount = rTabInfo.GetScTabCount(); nScTabIdx < nScTabCount; ++nScTabIdx )
683 // find real sheet index from the nScTabIdx counter
684 SCTAB nScTab = rTabInfo.GetRealScTab( nScTabIdx );
685 // create NAME records for all built-in names of this sheet
686 if( rTabInfo.IsExportTab( nScTab ) )
688 // *** 1) print ranges *** ----------------------------------------
690 if( rDoc.HasPrintRange() )
692 ScRangeList aRangeList;
693 for( sal_uInt16 nIdx = 0, nCount = rDoc.GetPrintRangeCount( nScTab ); nIdx < nCount; ++nIdx )
695 const ScRange* pPrintRange = rDoc.GetPrintRange( nScTab, nIdx );
696 if (!pPrintRange)
697 continue;
698 ScRange aRange( *pPrintRange );
699 // Calc document does not care about sheet index in print ranges
700 aRange.aStart.SetTab( nScTab );
701 aRange.aEnd.SetTab( nScTab );
702 aRange.PutInOrder();
703 aRangeList.push_back( aRange );
705 // create the NAME record (do not warn if ranges are shrunken)
706 GetAddressConverter().ValidateRangeList( aRangeList, false );
707 if( !aRangeList.empty() )
708 GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA, aRangeList );
711 // *** 2) print titles *** ----------------------------------------
713 ScRangeList aTitleList;
714 // repeated columns
715 if( const ScRange* pColRange = rDoc.GetRepeatColRange( nScTab ) )
716 aTitleList.push_back( ScRange(
717 pColRange->aStart.Col(), 0, nScTab,
718 pColRange->aEnd.Col(), GetXclMaxPos().Row(), nScTab ) );
719 // repeated rows
720 if( const ScRange* pRowRange = rDoc.GetRepeatRowRange( nScTab ) )
721 aTitleList.push_back( ScRange(
722 0, pRowRange->aStart.Row(), nScTab,
723 GetXclMaxPos().Col(), pRowRange->aEnd.Row(), nScTab ) );
724 // create the NAME record
725 GetAddressConverter().ValidateRangeList( aTitleList, false );
726 if( !aTitleList.empty() )
727 GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES, aTitleList );
729 // *** 3) filter ranges *** ---------------------------------------
731 if( GetBiff() == EXC_BIFF8 )
732 GetFilterManager().InitTabFilter( nScTab );
737 void XclExpNameManagerImpl::CreateUserNames()
739 std::vector<ScRangeData*> vEmulateAsLocalRange;
740 const ScRangeName& rNamedRanges = GetNamedRanges();
741 for (const auto& rEntry : rNamedRanges)
743 // skip definitions of shared formulas
744 if (!FindNamedExp(SCTAB_GLOBAL, rEntry.second->GetName()))
746 const ScTokenArray* pCode = rEntry.second->GetCode();
747 if ( pCode
748 && (rEntry.second->HasType( ScRangeData::Type::AbsPos ) || rEntry.second->HasType( ScRangeData::Type::AbsArea ))
749 && lcl_EnsureAbs3DToken( SCTAB_GLOBAL, pCode->FirstToken(), /*bFix=*/false ) )
751 vEmulateAsLocalRange.emplace_back(rEntry.second.get());
753 else
754 CreateName(SCTAB_GLOBAL, *rEntry.second);
757 //look at sheets containing local range names
758 ScRangeName::TabNameCopyMap rLocalNames;
759 GetDoc().GetAllTabRangeNames(rLocalNames);
760 for (const auto& [rTab, pRangeName] : rLocalNames)
762 for (const auto& rEntry : *pRangeName)
764 // skip definitions of shared formulas
765 if (!FindNamedExp(rTab, rEntry.second->GetName()))
766 CreateName(rTab, *rEntry.second);
770 // Emulate relative global variables by creating a copy in each local range.
771 // Creating AFTER true local range names so that conflicting global names will be ignored.
772 for ( SCTAB nTab = 0; nTab < GetDoc().GetTableCount(); ++nTab )
774 for ( auto rangeDataItr : vEmulateAsLocalRange )
776 if ( !FindNamedExp(nTab, rangeDataItr->GetName()) )
777 CreateName(nTab, *rangeDataItr );
782 XclExpNameManager::XclExpNameManager( const XclExpRoot& rRoot ) :
783 XclExpRoot( rRoot ),
784 mxImpl( new XclExpNameManagerImpl( rRoot ) )
788 XclExpNameManager::~XclExpNameManager()
792 void XclExpNameManager::Initialize()
794 mxImpl->Initialize();
797 sal_uInt16 XclExpNameManager::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
799 return mxImpl->InsertName( nTab, nScNameIdx, nCurrTab );
802 sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange )
804 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRange );
805 return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRange );
808 sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList )
810 sal_uInt16 nNameIdx = 0;
811 if( !rRangeList.empty() )
813 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRangeList );
814 nNameIdx = mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRangeList.front().aStart.Tab(), rRangeList );
816 return nNameIdx;
819 sal_uInt16 XclExpNameManager::InsertUniqueName(
820 const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
822 return mxImpl->InsertUniqueName( rName, xTokArr, nScTab );
825 sal_uInt16 XclExpNameManager::InsertRawName( const OUString& rName )
827 return mxImpl->InsertRawName( rName );
830 sal_uInt16 XclExpNameManager::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
832 return mxImpl->InsertMacroCall( rMacroName, bVBasic, bFunc, bHidden );
835 const OUString& XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx ) const
837 const XclExpName* pName = mxImpl->GetName( nNameIdx );
838 return pName ? pName->GetOrigName() : EMPTY_OUSTRING;
841 SCTAB XclExpNameManager::GetScTab( sal_uInt16 nNameIdx ) const
843 const XclExpName* pName = mxImpl->GetName( nNameIdx );
844 return pName ? pName->GetScTab() : SCTAB_GLOBAL;
847 bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx ) const
849 const XclExpName* pName = mxImpl->GetName( nNameIdx );
850 return pName && pName->IsVolatile();
853 void XclExpNameManager::Save( XclExpStream& rStrm )
855 mxImpl->Save( rStrm );
858 void XclExpNameManager::SaveXml( XclExpXmlStream& rStrm )
860 mxImpl->SaveXml( rStrm );
863 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */