1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include <document.hxx>
25 #include <rangenam.hxx>
26 #include <tokenarray.hxx>
27 #include <xehelper.hxx>
29 #include <excrecds.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>
38 #include <com/sun/star/sheet/NamedRangeFlag.hdl>
39 #include <xihelper.hxx>
41 using namespace ::oox
;
42 using namespace ::com::sun::star
;
44 // *** Helper classes ***
48 /** Represents an internal defined name, supports writing it to a NAME record. */
49 class XclExpName
: public XclExpRecord
, protected XclExpRoot
52 /** Creates a standard defined name. */
53 explicit XclExpName( const XclExpRoot
& rRoot
, const OUString
& rName
);
54 /** Creates a built-in defined name. */
55 explicit XclExpName( const XclExpRoot
& rRoot
, sal_Unicode cBuiltIn
);
57 /** Sets a token array containing the definition of this name. */
58 void SetTokenArray( const XclTokenArrayRef
& xTokArr
);
59 /** Changes this defined name to be local on the specified Calc sheet. */
60 void SetLocalTab( SCTAB nScTab
);
61 /** Hides or unhides the defined name. */
62 void SetHidden( bool bHidden
= true );
63 /** Changes this name to be the call to a VB macro function or procedure.
64 @param bVBasic true = Visual Basic macro, false = Sheet macro.
65 @param bFunc true = Macro function; false = Macro procedure. */
66 void SetMacroCall( bool bVBasic
, bool bFunc
);
68 /** Sets the name's symbol value
69 @param sValue the name's symbolic value */
70 void SetSymbol( const OUString
& rValue
);
72 /** Returns the original name (title) of this defined name. */
73 const OUString
& GetOrigName() const { return maOrigName
; }
74 /** Returns the Excel built-in name index of this defined name.
75 @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */
76 sal_Unicode
GetBuiltInName() const { return mcBuiltIn
; }
78 /** Returns the symbol value for this defined name. */
79 const OUString
& GetSymbol() const { return msSymbol
; }
81 /** Returns true, if this is a document-global defined name. */
82 bool IsGlobal() const { return mnXclTab
== EXC_NAME_GLOBAL
; }
83 /** Returns the Calc sheet of a local defined name. */
84 SCTAB
GetScTab() const { return mnScTab
; }
86 /** Returns true, if this defined name is volatile. */
87 bool IsVolatile() const;
88 /** Returns true, if this defined name describes a macro call.
89 @param bFunc true = Macro function; false = Macro procedure. */
90 bool IsMacroCall( bool bVBasic
, bool bFunc
) const;
92 /** Writes the entire NAME record to the passed stream. */
93 virtual void Save( XclExpStream
& rStrm
) override
;
95 virtual void SaveXml( XclExpXmlStream
& rStrm
) override
;
98 /** Writes the body of the NAME record to the passed stream. */
99 virtual void WriteBody( XclExpStream
& rStrm
) override
;
100 /** Convert localized range separators */
101 OUString
GetWithDefaultRangeSeparator( const OUString
& rSymbol
) const;
104 OUString maOrigName
; /// The original user-defined name.
105 OUString msSymbol
; /// The value of the symbol
106 XclExpStringRef mxName
; /// The name as Excel string object.
107 XclTokenArrayRef mxTokArr
; /// The definition of the defined name.
108 sal_Unicode mcBuiltIn
; /// The built-in index for built-in names.
109 SCTAB mnScTab
; /// The Calc sheet index for local names.
110 sal_uInt16 mnFlags
; /// Additional flags for this defined name.
111 sal_uInt16 mnExtSheet
; /// The 1-based index to a global EXTERNSHEET record.
112 sal_uInt16 mnXclTab
; /// The 1-based Excel sheet index for local names.
117 /** Implementation class of the name manager. */
118 class XclExpNameManagerImpl
: protected XclExpRoot
121 explicit XclExpNameManagerImpl( const XclExpRoot
& rRoot
);
123 /** Creates NAME records for built-in and user defined names. */
126 /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
127 sal_uInt16
InsertName( SCTAB nTab
, sal_uInt16 nScNameIdx
, SCTAB nCurrTab
);
129 /** Inserts a new built-in defined name. */
130 sal_uInt16
InsertBuiltInName( sal_Unicode cBuiltIn
, const XclTokenArrayRef
& xTokArr
, SCTAB nScTab
, const ScRangeList
& aRangeList
);
131 sal_uInt16
InsertBuiltInName( sal_Unicode cBuiltIn
, const XclTokenArrayRef
& xTokArr
, const ScRange
& aRange
);
132 /** Inserts a new defined name. Sets another unused name, if rName already exists. */
133 sal_uInt16
InsertUniqueName( const OUString
& rName
, const XclTokenArrayRef
& xTokArr
, SCTAB nScTab
);
134 /** Returns index of an existing name, or creates a name without definition. */
135 sal_uInt16
InsertRawName( const OUString
& rName
);
136 /** Searches or inserts a defined name describing a macro name.
137 @param bVBasic true = Visual Basic macro; false = Sheet macro.
138 @param bFunc true = Macro function; false = Macro procedure. */
139 sal_uInt16
InsertMacroCall( const OUString
& rMacroName
, bool bVBasic
, bool bFunc
, bool bHidden
);
141 /** Returns the NAME record at the specified position or 0 on error. */
142 const XclExpName
* GetName( sal_uInt16 nNameIdx
) const;
144 /** Writes the entire list of NAME records.
145 @descr In BIFF7 and lower, writes the entire global link table, which
146 consists of an EXTERNCOUNT record, several EXTERNSHEET records, and
147 the list of NAME records. */
148 void Save( XclExpStream
& rStrm
);
150 void SaveXml( XclExpXmlStream
& rStrm
);
153 typedef XclExpRecordList
< XclExpName
> XclExpNameList
;
154 typedef XclExpNameList::RecordRefType XclExpNameRef
;
156 typedef ::std::map
< ::std::pair
<SCTAB
, OUString
>, sal_uInt16
> NamedExpMap
;
160 * @param nTab 0-based table index, or SCTAB_GLOBAL for global names.
161 * @param nScIdx calc's name index.
163 * @return excel's name index.
165 sal_uInt16
FindNamedExp( SCTAB nTab
, const OUString
& sName
);
167 /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */
168 sal_uInt16
FindBuiltInNameIdx( const OUString
& rName
,
169 const OUString
& sSymbol
) const;
170 /** Returns an unused name for the passed name. */
171 OUString
GetUnusedName( const OUString
& rName
) const;
173 /** Appends a new NAME record to the record list.
174 @return The 1-based NAME record index used elsewhere in the Excel file. */
175 sal_uInt16
Append( XclExpName
* pName
);
176 sal_uInt16
Append( XclExpNameRef
const & rxName
) { return Append(rxName
.get()); }
177 /** Creates a new NAME record for the passed user-defined name.
178 @return The 1-based NAME record index used elsewhere in the Excel file. */
179 sal_uInt16
CreateName( SCTAB nTab
, const ScRangeData
& rRangeData
);
181 /** Creates NAME records for all built-in names in the document. */
182 void CreateBuiltInNames();
183 /** Creates NAME records for all user-defined names in the document. */
184 void CreateUserNames();
188 * Maps Calc's named range to Excel's NAME records. Global names use
189 * -1 as their table index, whereas sheet-local names have 0-based table
192 NamedExpMap maNamedExpMap
;
193 XclExpNameList maNameList
; /// List of NAME records.
194 size_t mnFirstUserIdx
; /// List index of first user-defined NAME record.
197 // *** Implementation ***
199 XclExpName::XclExpName( const XclExpRoot
& rRoot
, const OUString
& rName
) :
200 XclExpRecord( EXC_ID_NAME
),
203 mxName( XclExpStringHelper::CreateString( rRoot
, rName
, XclStrFlags::EightBitLength
) ),
204 mcBuiltIn( EXC_BUILTIN_UNKNOWN
),
205 mnScTab( SCTAB_GLOBAL
),
206 mnFlags( EXC_NAME_DEFAULT
),
207 mnExtSheet( EXC_NAME_GLOBAL
),
208 mnXclTab( EXC_NAME_GLOBAL
)
212 XclExpName::XclExpName( const XclExpRoot
& rRoot
, sal_Unicode cBuiltIn
) :
213 XclExpRecord( EXC_ID_NAME
),
215 mcBuiltIn( cBuiltIn
),
216 mnScTab( SCTAB_GLOBAL
),
217 mnFlags( EXC_NAME_DEFAULT
),
218 mnExtSheet( EXC_NAME_GLOBAL
),
219 mnXclTab( EXC_NAME_GLOBAL
)
221 // filter source range is hidden in Excel
222 if( cBuiltIn
== EXC_BUILTIN_FILTERDATABASE
)
225 // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag
226 if( (GetBiff() <= EXC_BIFF5
) && (cBuiltIn
== EXC_BUILTIN_FILTERDATABASE
) )
228 OUString
aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE
) );
229 mxName
= XclExpStringHelper::CreateString( rRoot
, aName
, XclStrFlags::EightBitLength
);
230 maOrigName
= XclTools::GetXclBuiltInDefName( cBuiltIn
);
234 maOrigName
= XclTools::GetBuiltInDefNameXml( cBuiltIn
) ;
235 mxName
= XclExpStringHelper::CreateString( rRoot
, cBuiltIn
, XclStrFlags::EightBitLength
);
236 ::set_flag( mnFlags
, EXC_NAME_BUILTIN
);
240 void XclExpName::SetTokenArray( const XclTokenArrayRef
& xTokArr
)
245 void XclExpName::SetLocalTab( SCTAB nScTab
)
247 OSL_ENSURE( GetTabInfo().IsExportTab( nScTab
), "XclExpName::SetLocalTab - invalid sheet index" );
248 if( !GetTabInfo().IsExportTab( nScTab
) )
252 GetGlobalLinkManager().FindExtSheet( mnExtSheet
, mnXclTab
, nScTab
);
254 // special handling for NAME record
257 case EXC_BIFF5
: // EXTERNSHEET index is positive in NAME record
258 mnExtSheet
= ~mnExtSheet
+ 1;
260 case EXC_BIFF8
: // EXTERNSHEET index not used, but must be created in link table
263 default: DBG_ERROR_BIFF();
266 // Excel sheet index is 1-based
270 void XclExpName::SetHidden( bool bHidden
)
272 ::set_flag( mnFlags
, EXC_NAME_HIDDEN
, bHidden
);
275 void XclExpName::SetMacroCall( bool bVBasic
, bool bFunc
)
277 ::set_flag( mnFlags
, EXC_NAME_PROC
);
278 ::set_flag( mnFlags
, EXC_NAME_VB
, bVBasic
);
279 ::set_flag( mnFlags
, EXC_NAME_FUNC
, bFunc
);
282 void XclExpName::SetSymbol( const OUString
& rSymbol
)
287 bool XclExpName::IsVolatile() const
289 return mxTokArr
&& mxTokArr
->IsVolatile();
292 bool XclExpName::IsMacroCall( bool bVBasic
, bool bFunc
) const
295 (::get_flag( mnFlags
, EXC_NAME_VB
) == bVBasic
) &&
296 (::get_flag( mnFlags
, EXC_NAME_FUNC
) == bFunc
);
299 void XclExpName::Save( XclExpStream
& rStrm
)
301 OSL_ENSURE( mxName
&& (mxName
->Len() > 0), "XclExpName::Save - missing name" );
302 OSL_ENSURE( !(IsGlobal() && ::get_flag( mnFlags
, EXC_NAME_BUILTIN
)), "XclExpName::Save - global built-in name" );
303 SetRecSize( 11 + mxName
->GetSize() + (mxTokArr
? mxTokArr
->GetSize() : 2) );
304 XclExpRecord::Save( rStrm
);
307 OUString
XclExpName::GetWithDefaultRangeSeparator( const OUString
& rSymbol
) const
309 sal_Int32 nPos
= rSymbol
.indexOf(';');
312 // convert with validation
314 ScAddress::Details
detailsXL( ::formula::FormulaGrammar::CONV_XL_A1
);
315 ScRefFlags nRes
= aRange
.Parse( rSymbol
.copy(0, nPos
), GetDoc(), detailsXL
);
316 if ( nRes
& ScRefFlags::VALID
)
318 nRes
= aRange
.Parse( rSymbol
.copy(nPos
+1), GetDoc(), detailsXL
);
319 if ( nRes
& ScRefFlags::VALID
)
321 return rSymbol
.replaceFirst(";", ",");
328 void XclExpName::SaveXml( XclExpXmlStream
& rStrm
)
330 sax_fastparser::FSHelperPtr
& rWorkbook
= rStrm
.GetCurrentStream();
331 rWorkbook
->startElement( XML_definedName
,
332 // OOXTODO: XML_comment, "",
333 // OOXTODO: XML_customMenu, "",
334 // OOXTODO: XML_description, "",
335 XML_function
, ToPsz( ::get_flag( mnFlags
, EXC_NAME_VB
) ),
336 // OOXTODO: XML_functionGroupId, "",
337 // OOXTODO: XML_help, "",
338 XML_hidden
, ToPsz( ::get_flag( mnFlags
, EXC_NAME_HIDDEN
) ),
339 XML_localSheetId
, sax_fastparser::UseIf(OString::number( mnScTab
), mnScTab
!= SCTAB_GLOBAL
),
340 XML_name
, maOrigName
.toUtf8(),
341 // OOXTODO: XML_publishToServer, "",
342 // OOXTODO: XML_shortcutKey, "",
343 // OOXTODO: XML_statusBar, "",
344 XML_vbProcedure
, ToPsz( ::get_flag( mnFlags
, EXC_NAME_VB
) )
345 // OOXTODO: XML_workbookParameter, "",
346 // OOXTODO: XML_xlm, ""
348 rWorkbook
->writeEscaped( GetWithDefaultRangeSeparator( msSymbol
) );
349 rWorkbook
->endElement( XML_definedName
);
352 void XclExpName::WriteBody( XclExpStream
& rStrm
)
354 sal_uInt16 nFmlaSize
= mxTokArr
? mxTokArr
->GetSize() : 0;
356 rStrm
<< mnFlags
// flags
357 << sal_uInt8( 0 ); // keyboard shortcut
358 mxName
->WriteLenField( rStrm
); // length of name
359 rStrm
<< nFmlaSize
// size of token array
360 << mnExtSheet
// BIFF5/7: EXTSHEET index, BIFF8: not used
361 << mnXclTab
// 1-based sheet index for local names
362 << sal_uInt32( 0 ); // length of menu/descr/help/status text
363 mxName
->WriteFlagField( rStrm
); // BIFF8 flag field (no-op in <=BIFF7)
364 mxName
->WriteBuffer( rStrm
); // character array of the name
366 mxTokArr
->WriteArray( rStrm
); // token array without size
369 /** Returns true (needed fixing) if FormulaToken was not absolute and 3D.
370 So, regardless of whether the fix was successful or not, true is still returned since a fix was required.*/
371 static bool lcl_EnsureAbs3DToken( const SCTAB nTab
, formula::FormulaToken
* pTok
, const bool bFix
= true )
373 bool bFixRequired
= false;
374 if ( !pTok
|| ( pTok
->GetType() != formula::svSingleRef
&& pTok
->GetType() != formula::svDoubleRef
) )
377 ScSingleRefData
* pRef1
= pTok
->GetSingleRef();
381 ScSingleRefData
* pRef2
= nullptr;
382 if ( pTok
->GetType() == formula::svDoubleRef
)
383 pRef2
= pTok
->GetSingleRef2();
385 if ( pRef1
->IsTabRel() || !pRef1
->IsFlag3D() )
390 if ( pRef1
->IsTabRel() && nTab
!= SCTAB_GLOBAL
)
391 pRef1
->SetAbsTab( nTab
+ pRef1
->Tab() ); //XLS requirement
392 if ( !pRef1
->IsTabRel() )
394 pRef1
->SetFlag3D( true ); //XLSX requirement
395 if ( pRef2
&& !pRef2
->IsTabRel() )
396 pRef2
->SetFlag3D( pRef2
->Tab() != pRef1
->Tab() );
401 if ( pRef2
&& pRef2
->IsTabRel() && !pRef1
->IsTabRel() )
404 if ( bFix
&& nTab
!= SCTAB_GLOBAL
)
406 pRef2
->SetAbsTab( nTab
+ pRef2
->Tab() );
407 pRef2
->SetFlag3D( pRef2
->Tab() != pRef1
->Tab() );
413 XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot
& rRoot
) :
419 void XclExpNameManagerImpl::Initialize()
421 CreateBuiltInNames();
422 mnFirstUserIdx
= maNameList
.GetSize();
426 sal_uInt16
XclExpNameManagerImpl::InsertName( SCTAB nTab
, sal_uInt16 nScNameIdx
, SCTAB nCurrTab
)
428 sal_uInt16 nNameIdx
= 0;
429 const ScRangeData
* pData
= nullptr;
430 ScRangeName
* pRN
= (nTab
== SCTAB_GLOBAL
) ? GetDoc().GetRangeName() : GetDoc().GetRangeName(nTab
);
432 pData
= pRN
->findByIndex(nScNameIdx
);
436 bool bEmulateGlobalRelativeTable
= false;
437 const ScTokenArray
* pCode
= pData
->GetCode();
439 && nTab
== SCTAB_GLOBAL
440 && (pData
->HasType( ScRangeData::Type::AbsPos
) || pData
->HasType( ScRangeData::Type::AbsArea
)) )
442 bEmulateGlobalRelativeTable
= lcl_EnsureAbs3DToken( nTab
, pCode
->FirstToken(), /*bFix=*/false );
444 nNameIdx
= FindNamedExp( bEmulateGlobalRelativeTable
? nCurrTab
: nTab
, pData
->GetName() );
446 nNameIdx
= CreateName(nTab
, *pData
);
452 sal_uInt16
XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn
, const XclTokenArrayRef
& xTokArr
, const ScRange
& aRange
)
454 XclExpNameRef xName
= new XclExpName( GetRoot(), cBuiltIn
);
455 xName
->SetTokenArray( xTokArr
);
456 xName
->SetLocalTab( aRange
.aStart
.Tab() );
457 OUString
sSymbol(aRange
.Format(GetDoc(), ScRefFlags::RANGE_ABS_3D
, ScAddress::Details( ::formula::FormulaGrammar::CONV_XL_A1
)));
458 xName
->SetSymbol( sSymbol
);
459 return Append( xName
);
462 sal_uInt16
XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn
, const XclTokenArrayRef
& xTokArr
, SCTAB nScTab
, const ScRangeList
& rRangeList
)
464 XclExpNameRef xName
= new XclExpName( GetRoot(), cBuiltIn
);
465 xName
->SetTokenArray( xTokArr
);
466 xName
->SetLocalTab( nScTab
);
468 rRangeList
.Format( sSymbol
, ScRefFlags::RANGE_ABS_3D
, GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1
);
469 xName
->SetSymbol( sSymbol
);
470 return Append( xName
);
473 sal_uInt16
XclExpNameManagerImpl::InsertUniqueName(
474 const OUString
& rName
, const XclTokenArrayRef
& xTokArr
, SCTAB nScTab
)
476 OSL_ENSURE( !rName
.isEmpty(), "XclExpNameManagerImpl::InsertUniqueName - empty name" );
477 XclExpNameRef xName
= new XclExpName( GetRoot(), GetUnusedName( rName
) );
478 xName
->SetTokenArray( xTokArr
);
479 xName
->SetLocalTab( nScTab
);
480 return Append( xName
);
483 sal_uInt16
XclExpNameManagerImpl::InsertRawName( const OUString
& rName
)
485 // empty name? may occur in broken external Calc tokens
486 if( rName
.isEmpty() )
489 // try to find an existing NAME record, regardless of its type
490 for( size_t nListIdx
= mnFirstUserIdx
, nListSize
= maNameList
.GetSize(); nListIdx
< nListSize
; ++nListIdx
)
492 XclExpNameRef xName
= maNameList
.GetRecord( nListIdx
);
493 if( xName
->IsGlobal() && (xName
->GetOrigName() == rName
) )
494 return static_cast< sal_uInt16
>( nListIdx
+ 1 );
497 // create a new NAME record
498 XclExpNameRef xName
= new XclExpName( GetRoot(), rName
);
499 return Append( xName
);
502 sal_uInt16
XclExpNameManagerImpl::InsertMacroCall( const OUString
& rMacroName
, bool bVBasic
, bool bFunc
, bool bHidden
)
504 // empty name? may occur in broken external Calc tokens
505 if( rMacroName
.isEmpty() )
508 // try to find an existing NAME record
509 for( size_t nListIdx
= mnFirstUserIdx
, nListSize
= maNameList
.GetSize(); nListIdx
< nListSize
; ++nListIdx
)
511 XclExpNameRef xName
= maNameList
.GetRecord( nListIdx
);
512 if( xName
->IsMacroCall( bVBasic
, bFunc
) && (xName
->GetOrigName() == rMacroName
) )
513 return static_cast< sal_uInt16
>( nListIdx
+ 1 );
516 // create a new NAME record
517 XclExpNameRef xName
= new XclExpName( GetRoot(), rMacroName
);
518 xName
->SetMacroCall( bVBasic
, bFunc
);
519 xName
->SetHidden( bHidden
);
521 // for sheet macros, add a #NAME! error
523 xName
->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME
) );
525 return Append( xName
);
528 const XclExpName
* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx
) const
530 OSL_ENSURE( maNameList
.HasRecord( nNameIdx
- 1 ), "XclExpNameManagerImpl::GetName - wrong record index" );
531 return maNameList
.GetRecord( nNameIdx
- 1 );
534 void XclExpNameManagerImpl::Save( XclExpStream
& rStrm
)
536 maNameList
.Save( rStrm
);
539 void XclExpNameManagerImpl::SaveXml( XclExpXmlStream
& rStrm
)
541 if( maNameList
.IsEmpty() )
543 sax_fastparser::FSHelperPtr
& rWorkbook
= rStrm
.GetCurrentStream();
544 rWorkbook
->startElement(XML_definedNames
);
545 maNameList
.SaveXml( rStrm
);
546 rWorkbook
->endElement( XML_definedNames
);
549 // private --------------------------------------------------------------------
551 sal_uInt16
XclExpNameManagerImpl::FindNamedExp( SCTAB nTab
, const OUString
& sName
)
553 NamedExpMap::key_type
key(nTab
, sName
);
554 NamedExpMap::const_iterator itr
= maNamedExpMap
.find(key
);
555 return (itr
== maNamedExpMap
.end()) ? 0 : itr
->second
;
558 sal_uInt16
XclExpNameManagerImpl::FindBuiltInNameIdx(
559 const OUString
& rName
, const OUString
& sSymbol
) const
561 /* Get built-in index from the name. Special case: the database range
562 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */
563 sal_Unicode cBuiltIn
= XclTools::GetBuiltInDefNameIndex( rName
);
565 if( cBuiltIn
< EXC_BUILTIN_UNKNOWN
)
567 // try to find the record in existing built-in NAME record list
568 for( size_t nPos
= 0; nPos
< mnFirstUserIdx
; ++nPos
)
570 XclExpNameRef xName
= maNameList
.GetRecord( nPos
);
571 if( xName
->GetBuiltInName() == cBuiltIn
&& xName
->GetSymbol().replace(';', ',') == sSymbol
.replace(';', ',') )
573 // tdf#112567 restore the original built-in names with non-localized separators
574 // TODO: support more localizations, if needed
575 if ( xName
->GetSymbol() != sSymbol
)
577 xName
->SetSymbol(xName
->GetSymbol().replace(';', ','));
579 return static_cast< sal_uInt16
>( nPos
+ 1 );
586 OUString
XclExpNameManagerImpl::GetUnusedName( const OUString
& rName
) const
588 OUString
aNewName( rName
);
589 sal_Int32 nAppIdx
= 0;
593 // search the list of user-defined names
595 for( size_t nPos
= mnFirstUserIdx
, nSize
= maNameList
.GetSize(); !bExist
&& (nPos
< nSize
); ++nPos
)
597 XclExpNameRef xName
= maNameList
.GetRecord( nPos
);
598 bExist
= xName
->GetOrigName() == aNewName
;
599 // name exists -> create a new name "<originalname>_<counter>"
601 aNewName
= rName
+ "_" + OUString::number( ++nAppIdx
);
607 sal_uInt16
XclExpNameManagerImpl::Append( XclExpName
* pName
)
609 if( maNameList
.GetSize() == 0xFFFF )
611 maNameList
.AppendRecord( pName
);
612 return static_cast< sal_uInt16
>( maNameList
.GetSize() ); // 1-based
615 sal_uInt16
XclExpNameManagerImpl::CreateName( SCTAB nTab
, const ScRangeData
& rRangeData
)
617 const OUString
& rName
= rRangeData
.GetName();
619 /* #i38821# recursive names: first insert the (empty) name object,
620 otherwise a recursive call of this function from the formula compiler
621 with the same defined name will not find it and will create it again. */
622 size_t nOldListSize
= maNameList
.GetSize();
623 XclExpNameRef xName
= new XclExpName( GetRoot(), rName
);
624 if (nTab
!= SCTAB_GLOBAL
)
625 xName
->SetLocalTab(nTab
);
626 sal_uInt16 nNameIdx
= Append( xName
);
627 // store the index of the NAME record in the lookup map
628 NamedExpMap::key_type
key(nTab
, rRangeData
.GetName());
629 maNamedExpMap
[key
] = nNameIdx
;
631 // Check if it is a hidden named range
632 if ((rRangeData
.GetUnoType() & sheet::NamedRangeFlag::HIDDEN
) == sheet::NamedRangeFlag::HIDDEN
)
633 xName
->SetHidden(true);
635 /* Create the definition formula.
636 This may cause recursive creation of other defined names. */
637 if( const ScTokenArray
* pScTokArr
= const_cast< ScRangeData
& >( rRangeData
).GetCode() )
639 XclTokenArrayRef xTokArr
;
641 // MSO requires named ranges to have absolute sheet references
642 if ( rRangeData
.HasType( ScRangeData::Type::AbsPos
) || rRangeData
.HasType( ScRangeData::Type::AbsArea
) )
644 // Don't modify the actual document; use a temporary copy to create the export formulas.
645 ScTokenArray
aTokenCopy( pScTokArr
->CloneValue() );
646 lcl_EnsureAbs3DToken(nTab
, aTokenCopy
.FirstToken());
648 xTokArr
= GetFormulaCompiler().CreateFormula(EXC_FMLATYPE_NAME
, aTokenCopy
);
649 if ( GetOutput() != EXC_OUTPUT_BINARY
)
651 ScCompiler
aComp(GetDoc(), rRangeData
.GetPos(), aTokenCopy
,
652 formula::FormulaGrammar::GRAM_OOXML
);
653 aComp
.CreateStringFromTokenArray( sSymbol
);
658 bool bOOXML
= GetOutput() == EXC_OUTPUT_XML_2007
;
659 xTokArr
= GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME
, *pScTokArr
, bOOXML
?
660 &rRangeData
.GetPos() : nullptr );
661 sSymbol
= rRangeData
.GetSymbol( (GetOutput() == EXC_OUTPUT_BINARY
) ?
662 formula::FormulaGrammar::GRAM_ENGLISH_XL_A1
: formula::FormulaGrammar::GRAM_OOXML
);
664 xName
->SetTokenArray( xTokArr
);
665 xName
->SetSymbol( sSymbol
);
667 /* Try to replace by existing built-in name - complete token array is
668 needed for comparison, and due to the recursion problem above this
669 cannot be done earlier. If a built-in name is found, the created NAME
670 record for this name and all following records in the list must be
671 deleted, otherwise they may contain wrong name list indexes. */
672 sal_uInt16 nBuiltInIdx
= FindBuiltInNameIdx( rName
, sSymbol
);
673 if( nBuiltInIdx
!= 0 )
675 // delete the new NAME records
676 while( maNameList
.GetSize() > nOldListSize
)
677 maNameList
.RemoveRecord( maNameList
.GetSize() - 1 );
678 // use index of the found built-in NAME record
679 key
= NamedExpMap::key_type(nTab
, rRangeData
.GetName());
680 maNamedExpMap
[key
] = nNameIdx
= nBuiltInIdx
;
687 void XclExpNameManagerImpl::CreateBuiltInNames()
689 ScDocument
& rDoc
= GetDoc();
690 XclExpTabInfo
& rTabInfo
= GetTabInfo();
692 /* #i2394# built-in defined names must be sorted by the name of the
693 containing sheet. Example: SheetA!Print_Range must be stored *before*
694 SheetB!Print_Range, regardless of the position of SheetA in the document! */
695 for( SCTAB nScTabIdx
= 0, nScTabCount
= rTabInfo
.GetScTabCount(); nScTabIdx
< nScTabCount
; ++nScTabIdx
)
697 // find real sheet index from the nScTabIdx counter
698 SCTAB nScTab
= rTabInfo
.GetRealScTab( nScTabIdx
);
699 // create NAME records for all built-in names of this sheet
700 if( rTabInfo
.IsExportTab( nScTab
) )
702 // *** 1) print ranges *** ----------------------------------------
704 if( rDoc
.HasPrintRange() )
706 ScRangeList aRangeList
;
707 for( sal_uInt16 nIdx
= 0, nCount
= rDoc
.GetPrintRangeCount( nScTab
); nIdx
< nCount
; ++nIdx
)
709 const ScRange
* pPrintRange
= rDoc
.GetPrintRange( nScTab
, nIdx
);
712 ScRange
aRange( *pPrintRange
);
713 // Calc document does not care about sheet index in print ranges
714 aRange
.aStart
.SetTab( nScTab
);
715 aRange
.aEnd
.SetTab( nScTab
);
718 // tdf#148170 - convert print range to an excel cell range
719 XclRange
aXclRange(ScAddress::UNINITIALIZED
);
720 // create no warning if ranges are shrunken
721 if (GetAddressConverter().ConvertRange(aXclRange
, aRange
, false))
723 XclImpAddressConverter::FillRange(aXclRange
, aRange
);
724 aRangeList
.push_back(aRange
);
727 // create the NAME record
728 if( !aRangeList
.empty() )
729 GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA
, aRangeList
);
732 // *** 2) print titles *** ----------------------------------------
734 ScRangeList aTitleList
;
736 if( std::optional
<ScRange
> oColRange
= rDoc
.GetRepeatColRange( nScTab
) )
737 aTitleList
.push_back( ScRange(
738 oColRange
->aStart
.Col(), 0, nScTab
,
739 oColRange
->aEnd
.Col(), GetXclMaxPos().Row(), nScTab
) );
741 if( std::optional
<ScRange
> oRowRange
= rDoc
.GetRepeatRowRange( nScTab
) )
742 aTitleList
.push_back( ScRange(
743 0, oRowRange
->aStart
.Row(), nScTab
,
744 GetXclMaxPos().Col(), oRowRange
->aEnd
.Row(), nScTab
) );
745 // create the NAME record
746 GetAddressConverter().ValidateRangeList( aTitleList
, false );
747 if( !aTitleList
.empty() )
748 GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES
, aTitleList
);
750 // *** 3) filter ranges *** ---------------------------------------
752 if( GetBiff() == EXC_BIFF8
)
753 GetFilterManager().InitTabFilter( nScTab
);
758 void XclExpNameManagerImpl::CreateUserNames()
760 std::vector
<ScRangeData
*> vEmulateAsLocalRange
;
761 const ScRangeName
& rNamedRanges
= GetNamedRanges();
762 for (const auto& rEntry
: rNamedRanges
)
764 // skip definitions of shared formulas
765 if (!FindNamedExp(SCTAB_GLOBAL
, rEntry
.second
->GetName()))
767 const ScTokenArray
* pCode
= rEntry
.second
->GetCode();
769 && (rEntry
.second
->HasType( ScRangeData::Type::AbsPos
) || rEntry
.second
->HasType( ScRangeData::Type::AbsArea
))
770 && lcl_EnsureAbs3DToken( SCTAB_GLOBAL
, pCode
->FirstToken(), /*bFix=*/false ) )
772 vEmulateAsLocalRange
.emplace_back(rEntry
.second
.get());
775 CreateName(SCTAB_GLOBAL
, *rEntry
.second
);
778 //look at sheets containing local range names
779 ScRangeName::TabNameCopyMap rLocalNames
;
780 GetDoc().GetAllTabRangeNames(rLocalNames
);
781 for (const auto& [rTab
, pRangeName
] : rLocalNames
)
783 for (const auto& rEntry
: *pRangeName
)
785 // skip definitions of shared formulas
786 if (!FindNamedExp(rTab
, rEntry
.second
->GetName()))
787 CreateName(rTab
, *rEntry
.second
);
791 // Emulate relative global variables by creating a copy in each local range.
792 // Creating AFTER true local range names so that conflicting global names will be ignored.
793 for ( SCTAB nTab
= 0; nTab
< GetDoc().GetTableCount(); ++nTab
)
795 for ( auto rangeDataItr
: vEmulateAsLocalRange
)
797 if ( !FindNamedExp(nTab
, rangeDataItr
->GetName()) )
798 CreateName(nTab
, *rangeDataItr
);
803 XclExpNameManager::XclExpNameManager( const XclExpRoot
& rRoot
) :
805 mxImpl( std::make_shared
<XclExpNameManagerImpl
>( rRoot
) )
809 XclExpNameManager::~XclExpNameManager()
813 void XclExpNameManager::Initialize()
815 mxImpl
->Initialize();
818 sal_uInt16
XclExpNameManager::InsertName( SCTAB nTab
, sal_uInt16 nScNameIdx
, SCTAB nCurrTab
)
820 return mxImpl
->InsertName( nTab
, nScNameIdx
, nCurrTab
);
823 sal_uInt16
XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn
, const ScRange
& rRange
)
825 XclTokenArrayRef xTokArr
= GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME
, rRange
);
826 return mxImpl
->InsertBuiltInName( cBuiltIn
, xTokArr
, rRange
);
829 sal_uInt16
XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn
, const ScRangeList
& rRangeList
)
831 sal_uInt16 nNameIdx
= 0;
832 if( !rRangeList
.empty() )
834 XclTokenArrayRef xTokArr
= GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME
, rRangeList
);
835 nNameIdx
= mxImpl
->InsertBuiltInName( cBuiltIn
, xTokArr
, rRangeList
.front().aStart
.Tab(), rRangeList
);
840 sal_uInt16
XclExpNameManager::InsertUniqueName(
841 const OUString
& rName
, const XclTokenArrayRef
& xTokArr
, SCTAB nScTab
)
843 return mxImpl
->InsertUniqueName( rName
, xTokArr
, nScTab
);
846 sal_uInt16
XclExpNameManager::InsertRawName( const OUString
& rName
)
848 return mxImpl
->InsertRawName( rName
);
851 sal_uInt16
XclExpNameManager::InsertMacroCall( const OUString
& rMacroName
, bool bVBasic
, bool bFunc
, bool bHidden
)
853 return mxImpl
->InsertMacroCall( rMacroName
, bVBasic
, bFunc
, bHidden
);
856 OUString
XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx
) const
858 const XclExpName
* pName
= mxImpl
->GetName( nNameIdx
);
859 return pName
? pName
->GetOrigName() : OUString();
862 SCTAB
XclExpNameManager::GetScTab( sal_uInt16 nNameIdx
) const
864 const XclExpName
* pName
= mxImpl
->GetName( nNameIdx
);
865 return pName
? pName
->GetScTab() : SCTAB_GLOBAL
;
868 bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx
) const
870 const XclExpName
* pName
= mxImpl
->GetName( nNameIdx
);
871 return pName
&& pName
->IsVolatile();
874 void XclExpNameManager::Save( XclExpStream
& rStrm
)
876 mxImpl
->Save( rStrm
);
879 void XclExpNameManager::SaveXml( XclExpXmlStream
& rStrm
)
881 mxImpl
->SaveXml( rStrm
);
884 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */