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 .
20 #include "defnamesbuffer.hxx"
22 #include <com/sun/star/sheet/ComplexReference.hpp>
23 #include <com/sun/star/sheet/ExternalReference.hpp>
24 #include <com/sun/star/sheet/NamedRangeFlag.hpp>
25 #include <com/sun/star/sheet/ReferenceFlags.hpp>
26 #include <com/sun/star/sheet/SingleReference.hpp>
27 #include <com/sun/star/sheet/XFormulaTokens.hpp>
28 #include <com/sun/star/sheet/XPrintAreas.hpp>
29 #include <osl/diagnose.h>
30 #include <rtl/ustrbuf.hxx>
31 #include <oox/helper/attributelist.hxx>
32 #include <oox/helper/propertyset.hxx>
33 #include "addressconverter.hxx"
34 #include "biffinputstream.hxx"
35 #include "externallinkbuffer.hxx"
36 #include "formulaparser.hxx"
37 #include "worksheetbuffer.hxx"
38 #include "tokenarray.hxx"
39 #include "tokenuno.hxx"
40 #include "compiler.hxx"
45 using namespace ::com::sun::star::sheet
;
46 using namespace ::com::sun::star::table
;
47 using namespace ::com::sun::star::uno
;
51 const sal_uInt32 BIFF12_DEFNAME_HIDDEN
= 0x00000001;
52 const sal_uInt32 BIFF12_DEFNAME_FUNC
= 0x00000002;
53 const sal_uInt32 BIFF12_DEFNAME_VBNAME
= 0x00000004;
54 const sal_uInt32 BIFF12_DEFNAME_MACRO
= 0x00000008;
55 const sal_uInt32 BIFF12_DEFNAME_BUILTIN
= 0x00000020;
57 const sal_uInt16 BIFF_REFFLAG_COL1REL
= 0x0001;
58 const sal_uInt16 BIFF_REFFLAG_ROW1REL
= 0x0002;
59 const sal_uInt16 BIFF_REFFLAG_COL2REL
= 0x0004;
60 const sal_uInt16 BIFF_REFFLAG_ROW2REL
= 0x0008;
62 const sal_Char
* const spcOoxPrefix
= "_xlnm.";
64 const sal_Char
* const sppcBaseNames
[] =
82 OUString
lclGetBaseName( sal_Unicode cBuiltinId
)
84 OSL_ENSURE( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
), "lclGetBaseName - unsupported built-in identifier" );
85 OUStringBuffer aBuffer
;
86 if( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
) )
87 aBuffer
.appendAscii( sppcBaseNames
[ cBuiltinId
] );
89 aBuffer
.append( static_cast< sal_Int32
>( cBuiltinId
) );
90 return aBuffer
.makeStringAndClear();
93 OUString
lclGetPrefixedName( sal_Unicode cBuiltinId
)
95 return OUStringBuffer().appendAscii( spcOoxPrefix
).append( lclGetBaseName( cBuiltinId
) ).makeStringAndClear();
98 /** returns the built-in name identifier from a perfixed built-in name, e.g. '_xlnm.Print_Area'. */
99 sal_Unicode
lclGetBuiltinIdFromPrefixedName( const OUString
& rModelName
)
101 OUString aPrefix
= OUString::createFromAscii( spcOoxPrefix
);
102 sal_Int32 nPrefixLen
= aPrefix
.getLength();
103 if( rModelName
.matchIgnoreAsciiCase( aPrefix
) )
105 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
107 OUString aBaseName
= lclGetBaseName( cBuiltinId
);
108 sal_Int32 nBaseNameLen
= aBaseName
.getLength();
109 if( (rModelName
.getLength() == nPrefixLen
+ nBaseNameLen
) && rModelName
.matchIgnoreAsciiCase( aBaseName
, nPrefixLen
) )
113 return BIFF_DEFNAME_UNKNOWN
;
116 /** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */
117 sal_Unicode
lclGetBuiltinIdFromBaseName( const OUString
& rModelName
)
119 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
120 if( rModelName
.equalsIgnoreAsciiCaseAscii( sppcBaseNames
[ cBuiltinId
] ) )
122 return BIFF_DEFNAME_UNKNOWN
;
125 OUString
lclGetUpcaseModelName( const OUString
& rModelName
)
128 return rModelName
.toAsciiUpperCase();
131 void lclConvertRefFlags( sal_Int32
& ornFlags
, sal_Int32
& ornAbsPos
, sal_Int32
& ornRelPos
, sal_Int32 nBasePos
, sal_Int32 nApiRelFlag
, bool bRel
)
133 if( getFlag( ornFlags
, nApiRelFlag
) && !bRel
)
135 // convert relative to absolute
136 setFlag( ornFlags
, nApiRelFlag
, false );
137 ornAbsPos
= nBasePos
+ ornRelPos
;
139 else if( !getFlag( ornFlags
, nApiRelFlag
) && bRel
)
141 // convert absolute to relative
142 setFlag( ornFlags
, nApiRelFlag
, true );
143 ornRelPos
= ornAbsPos
- nBasePos
;
147 void lclConvertSingleRefFlags( SingleReference
& orApiRef
, const CellAddress
& rBaseAddr
, bool bColRel
, bool bRowRel
)
149 using namespace ::com::sun::star::sheet::ReferenceFlags
;
151 orApiRef
.Flags
, orApiRef
.Column
, orApiRef
.RelativeColumn
,
152 rBaseAddr
.Column
, COLUMN_RELATIVE
, bColRel
);
154 orApiRef
.Flags
, orApiRef
.Row
, orApiRef
.RelativeRow
,
155 rBaseAddr
.Row
, ROW_RELATIVE
, bRowRel
);
158 Any
lclConvertReference( const Any
& rRefAny
, const CellAddress
& rBaseAddr
, sal_uInt16 nRelFlags
)
160 if( rRefAny
.has
< SingleReference
>() && !getFlag( nRelFlags
, BIFF_REFFLAG_COL2REL
) && !getFlag( nRelFlags
, BIFF_REFFLAG_ROW2REL
) )
162 SingleReference aApiRef
;
164 lclConvertSingleRefFlags( aApiRef
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL1REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW1REL
) );
165 return Any( aApiRef
);
167 if( rRefAny
.has
< ComplexReference
>() )
169 ComplexReference aApiRef
;
171 lclConvertSingleRefFlags( aApiRef
.Reference1
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL1REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW1REL
) );
172 lclConvertSingleRefFlags( aApiRef
.Reference2
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL2REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW2REL
) );
173 return Any( aApiRef
);
180 DefinedNameModel::DefinedNameModel() :
190 DefinedNameBase::DefinedNameBase( const WorkbookHelper
& rHelper
) :
191 WorkbookHelper( rHelper
)
195 const OUString
& DefinedNameBase::getUpcaseModelName() const
197 if( maUpModelName
.isEmpty() )
198 maUpModelName
= lclGetUpcaseModelName( maModel
.maName
);
199 return maUpModelName
;
202 Any
DefinedNameBase::getReference( const CellAddress
& rBaseAddr
) const
204 if( maRefAny
.hasValue() && (maModel
.maName
.getLength() >= 2) && (maModel
.maName
[ 0 ] == '\x01') )
206 sal_Unicode cFlagsChar
= getUpcaseModelName()[ 1 ];
207 if( ('A' <= cFlagsChar
) && (cFlagsChar
<= 'P') )
209 sal_uInt16 nRelFlags
= static_cast< sal_uInt16
>( cFlagsChar
- 'A' );
210 if( maRefAny
.has
< ExternalReference
>() )
212 ExternalReference aApiExtRef
;
213 maRefAny
>>= aApiExtRef
;
214 Any aRefAny
= lclConvertReference( aApiExtRef
.Reference
, rBaseAddr
, nRelFlags
);
215 if( aRefAny
.hasValue() )
217 aApiExtRef
.Reference
<<= aRefAny
;
218 return Any( aApiExtRef
);
223 return lclConvertReference( maRefAny
, rBaseAddr
, nRelFlags
);
230 ApiTokenSequence
DefinedNameBase::importOoxFormula( sal_Int16 nBaseSheet
)
232 return (!maModel
.maFormula
.isEmpty()) ?
233 getFormulaParser().importFormula( CellAddress( nBaseSheet
, 0, 0 ), maModel
.maFormula
) :
234 getFormulaParser().convertErrorToFormula( BIFF_ERR_NAME
);
237 ApiTokenSequence
DefinedNameBase::importBiff12Formula( sal_Int16 nBaseSheet
, SequenceInputStream
& rStrm
)
239 return getFormulaParser().importFormula( CellAddress( nBaseSheet
, 0, 0 ), FORMULATYPE_DEFINEDNAME
, rStrm
);
242 ApiTokenSequence
DefinedNameBase::importBiffFormula( sal_Int16 nBaseSheet
, BiffInputStream
& rStrm
, const sal_uInt16
* pnFmlaSize
)
244 return (!pnFmlaSize
|| (*pnFmlaSize
> 0)) ?
245 getFormulaParser().importFormula( CellAddress( nBaseSheet
, 0, 0 ), FORMULATYPE_DEFINEDNAME
, rStrm
, pnFmlaSize
) :
246 getFormulaParser().convertErrorToFormula( BIFF_ERR_NAME
);
249 DefinedName::DefinedName( const WorkbookHelper
& rHelper
) :
250 DefinedNameBase( rHelper
),
254 mcBuiltinId( BIFF_DEFNAME_UNKNOWN
),
259 void DefinedName::importDefinedName( const AttributeList
& rAttribs
)
261 maModel
.maName
= rAttribs
.getXString( XML_name
, OUString() );
262 maModel
.mnSheet
= rAttribs
.getInteger( XML_localSheetId
, -1 );
263 maModel
.mnFuncGroupId
= rAttribs
.getInteger( XML_functionGroupId
, -1 );
264 maModel
.mbMacro
= rAttribs
.getBool( XML_xlm
, false );
265 maModel
.mbFunction
= rAttribs
.getBool( XML_function
, false );
266 maModel
.mbVBName
= rAttribs
.getBool( XML_vbProcedure
, false );
267 maModel
.mbHidden
= rAttribs
.getBool( XML_hidden
, false );
268 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
270 /* Detect built-in state from name itself, there is no built-in flag.
271 Built-in names are prexixed with '_xlnm.' instead. */
272 mcBuiltinId
= lclGetBuiltinIdFromPrefixedName( maModel
.maName
);
275 void DefinedName::setFormula( const OUString
& rFormula
)
277 maModel
.maFormula
= rFormula
;
280 void DefinedName::importDefinedName( SequenceInputStream
& rStrm
)
283 nFlags
= rStrm
.readuInt32();
284 rStrm
.skip( 1 ); // keyboard shortcut
285 maModel
.mnSheet
= rStrm
.readInt32();
286 rStrm
>> maModel
.maName
;
287 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
289 // macro function/command, hidden flag
290 maModel
.mnFuncGroupId
= extractValue
< sal_Int32
>( nFlags
, 6, 9 );
291 maModel
.mbMacro
= getFlag( nFlags
, BIFF12_DEFNAME_MACRO
);
292 maModel
.mbFunction
= getFlag( nFlags
, BIFF12_DEFNAME_FUNC
);
293 maModel
.mbVBName
= getFlag( nFlags
, BIFF12_DEFNAME_VBNAME
);
294 maModel
.mbHidden
= getFlag( nFlags
, BIFF12_DEFNAME_HIDDEN
);
296 // get built-in name index from name
297 if( getFlag( nFlags
, BIFF12_DEFNAME_BUILTIN
) )
298 mcBuiltinId
= lclGetBuiltinIdFromBaseName( maModel
.maName
);
300 // store token array data
301 sal_Int64 nRecPos
= rStrm
.tell();
302 sal_Int32 nFmlaSize
= rStrm
.readInt32();
303 rStrm
.skip( nFmlaSize
);
304 sal_Int32 nAddDataSize
= rStrm
.readInt32();
305 if( !rStrm
.isEof() && (nFmlaSize
> 0) && (nAddDataSize
>= 0) && (rStrm
.getRemaining() >= nAddDataSize
) )
307 sal_Int32 nTotalSize
= 8 + nFmlaSize
+ nAddDataSize
;
308 mxFormula
.reset( new StreamDataSequence
);
309 rStrm
.seek( nRecPos
);
310 rStrm
.readData( *mxFormula
, nTotalSize
);
314 void DefinedName::createNameObject( sal_Int32 nIndex
)
316 // do not create names for (macro) functions or VBA procedures
317 // #163146# do not ignore hidden names (may be regular names created by VBA scripts)
318 if( /*maModel.mbHidden ||*/ maModel
.mbFunction
|| maModel
.mbVBName
)
321 // skip BIFF names without stream position (e.g. BIFF3-BIFF4 internal 3D references)
322 if( (getFilterType() == FILTER_BIFF
) && !mxBiffStrm
.get() )
325 // convert original name to final Calc name (TODO: filter invalid characters from model name)
326 maCalcName
= isBuiltinName() ? lclGetPrefixedName( mcBuiltinId
) : maModel
.maName
;
328 // #163146# do not rename sheet-local names by default, this breaks VBA scripts
330 // special flags for this name
331 sal_Int32 nNameFlags
= 0;
332 using namespace ::com::sun::star::sheet::NamedRangeFlag
;
333 if( !isGlobalName() ) switch( mcBuiltinId
)
335 case BIFF_DEFNAME_CRITERIA
: nNameFlags
= FILTER_CRITERIA
; break;
336 case BIFF_DEFNAME_PRINTAREA
: nNameFlags
= PRINT_AREA
; break;
337 case BIFF_DEFNAME_PRINTTITLES
: nNameFlags
= COLUMN_HEADER
| ROW_HEADER
; break;
340 // create the name and insert it into the document, maCalcName will be changed to the resulting name
341 if (maModel
.mnSheet
>= 0)
342 mpScRangeData
= createLocalNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
, maModel
.mnSheet
);
344 mpScRangeData
= createNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
);
345 mnTokenIndex
= nIndex
;
349 DefinedName::getTokens()
351 // convert and set formula of the defined name
352 ApiTokenSequence aTokens
;
353 switch( getFilterType() )
357 if( mxFormula
.get() )
359 SequenceInputStream
aStrm( *mxFormula
);
360 aTokens
= importBiff12Formula( mnCalcSheet
, aStrm
);
363 aTokens
= importOoxFormula( mnCalcSheet
);
368 OSL_ENSURE( mxBiffStrm
.get(), "DefinedName::convertFormula - missing BIFF stream" );
369 if( mxBiffStrm
.get() )
371 BiffInputStream
& rStrm
= mxBiffStrm
->getStream();
372 BiffInputStreamPosGuard
aStrmGuard( rStrm
);
373 if( mxBiffStrm
->restorePosition() )
374 aTokens
= importBiffFormula( mnCalcSheet
, rStrm
, &mnFmlaSize
);
384 std::unique_ptr
<ScTokenArray
> DefinedName::getScTokens()
386 ScTokenArray aTokenArray
;
387 ScCompiler
aCompiler(&getScDocument(), ScAddress(0, 0, mnCalcSheet
));
388 aCompiler
.SetGrammar(formula::FormulaGrammar::GRAM_OOXML
);
389 std::unique_ptr
<ScTokenArray
> pArray(aCompiler
.CompileString(maModel
.maFormula
));
390 // Compile the tokens into RPN once to populate information into tokens
391 // where necessary, e.g. for TableRef inner reference. RPN can be discarded
392 // after, a resulting error must be reset.
393 sal_uInt16 nErr
= pArray
->GetCodeError();
394 aCompiler
.CompileTokenArray();
396 pArray
->SetCodeError(nErr
);
401 void DefinedName::convertFormula()
403 // macro function or vba procedure
407 // convert and set formula of the defined name
408 if ( getFilterType() == FILTER_OOXML
)
410 std::unique_ptr
<ScTokenArray
> pTokenArray
= getScTokens();
411 mpScRangeData
->SetCode( *pTokenArray
);
414 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
415 Sequence
< FormulaToken
> aFTokenSeq
;
416 (void)ScTokenConversion::ConvertToTokenSequence( this->getScDocument(), aFTokenSeq
, *pTokenArray
);
417 // set built-in names (print ranges, repeated titles, filter ranges)
418 if( !isGlobalName() ) switch( mcBuiltinId
)
420 case BIFF_DEFNAME_PRINTAREA
:
422 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
423 ApiCellRangeList aPrintRanges
;
424 getFormulaParser().extractCellRangeList( aPrintRanges
, aFTokenSeq
, false, mnCalcSheet
);
425 if( xPrintAreas
.is() && !aPrintRanges
.empty() )
426 xPrintAreas
->setPrintAreas( aPrintRanges
.toSequence() );
429 case BIFF_DEFNAME_PRINTTITLES
:
431 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
432 ApiCellRangeList aTitleRanges
;
433 getFormulaParser().extractCellRangeList( aTitleRanges
, aFTokenSeq
, false, mnCalcSheet
);
434 if( xPrintAreas
.is() && !aTitleRanges
.empty() )
436 bool bHasRowTitles
= false;
437 bool bHasColTitles
= false;
438 const CellAddress
& rMaxPos
= getAddressConverter().getMaxAddress();
439 for( ::std::vector
< CellRangeAddress
>::const_iterator aIt
= aTitleRanges
.begin(), aEnd
= aTitleRanges
.end(); (aIt
!= aEnd
) && (!bHasRowTitles
|| !bHasColTitles
); ++aIt
)
441 bool bFullRow
= (aIt
->StartColumn
== 0) && (aIt
->EndColumn
>= rMaxPos
.Column
);
442 bool bFullCol
= (aIt
->StartRow
== 0) && (aIt
->EndRow
>= rMaxPos
.Row
);
443 if( !bHasRowTitles
&& bFullRow
&& !bFullCol
)
445 xPrintAreas
->setTitleRows( *aIt
);
446 xPrintAreas
->setPrintTitleRows( sal_True
);
447 bHasRowTitles
= true;
449 else if( !bHasColTitles
&& bFullCol
&& !bFullRow
)
451 xPrintAreas
->setTitleColumns( *aIt
);
452 xPrintAreas
->setPrintTitleColumns( sal_True
);
453 bHasColTitles
= true;
462 bool DefinedName::getAbsoluteRange( CellRangeAddress
& orRange
) const
464 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
465 Sequence
< FormulaToken
> aFTokenSeq
;
466 ScTokenConversion::ConvertToTokenSequence(getScDocument(), aFTokenSeq
, *pTokenArray
);
467 return getFormulaParser().extractCellRange( orRange
, aFTokenSeq
, false );
470 DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper
& rHelper
) :
471 WorkbookHelper( rHelper
)
475 DefinedNameRef
DefinedNamesBuffer::importDefinedName( const AttributeList
& rAttribs
)
477 DefinedNameRef xDefName
= createDefinedName();
478 xDefName
->importDefinedName( rAttribs
);
482 void DefinedNamesBuffer::importDefinedName( SequenceInputStream
& rStrm
)
484 createDefinedName()->importDefinedName( rStrm
);
487 void DefinedNamesBuffer::finalizeImport()
489 // first insert all names without formula definition into the document, and insert them into the maps
491 for( DefNameVector::iterator aIt
= maDefNames
.begin(), aEnd
= maDefNames
.end(); aIt
!= aEnd
; ++aIt
)
493 DefinedNameRef xDefName
= *aIt
;
494 xDefName
->createNameObject( ++index
);
495 // map by sheet index and original model name
496 maModelNameMap
[ SheetNameKey( xDefName
->getLocalCalcSheet(), xDefName
->getUpcaseModelName() ) ] = xDefName
;
497 // map by sheet index and built-in identifier
498 if( !xDefName
->isGlobalName() && xDefName
->isBuiltinName() )
499 maBuiltinMap
[ BuiltinKey( xDefName
->getLocalCalcSheet(), xDefName
->getBuiltinId() ) ] = xDefName
;
500 // map by API formula token identifier
501 sal_Int32 nTokenIndex
= xDefName
->getTokenIndex();
502 if( nTokenIndex
>= 0 )
503 maTokenIdMap
[ nTokenIndex
] = xDefName
;
506 /* Now convert all name formulas, so that the formula parser can find all
507 names in case of circular dependencies. */
508 maDefNames
.forEachMem( &DefinedName::convertFormula
);
511 DefinedNameRef
DefinedNamesBuffer::getByIndex( sal_Int32 nIndex
) const
513 return maDefNames
.get( nIndex
);
516 DefinedNameRef
DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex
) const
518 return maTokenIdMap
.get( nIndex
);
521 DefinedNameRef
DefinedNamesBuffer::getByModelName( const OUString
& rModelName
, sal_Int16 nCalcSheet
) const
523 OUString aUpcaseName
= lclGetUpcaseModelName( rModelName
);
524 DefinedNameRef xDefName
= maModelNameMap
.get( SheetNameKey( nCalcSheet
, aUpcaseName
) );
525 // lookup global name, if no local name exists
526 if( !xDefName
&& (nCalcSheet
>= 0) )
527 xDefName
= maModelNameMap
.get( SheetNameKey( -1, aUpcaseName
) );
531 DefinedNameRef
DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId
, sal_Int16 nCalcSheet
) const
533 return maBuiltinMap
.get( BuiltinKey( nCalcSheet
, cBuiltinId
) );
536 DefinedNameRef
DefinedNamesBuffer::createDefinedName()
538 DefinedNameRef
xDefName( new DefinedName( *this ) );
539 maDefNames
.push_back( xDefName
);
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */