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 <oox/token/tokens.hxx>
34 #include "addressconverter.hxx"
35 #include "biffinputstream.hxx"
36 #include "externallinkbuffer.hxx"
37 #include "formulaparser.hxx"
38 #include "worksheetbuffer.hxx"
39 #include "tokenarray.hxx"
40 #include "tokenuno.hxx"
41 #include "compiler.hxx"
46 using namespace ::com::sun::star::sheet
;
47 using namespace ::com::sun::star::table
;
48 using namespace ::com::sun::star::uno
;
52 const sal_uInt32 BIFF12_DEFNAME_HIDDEN
= 0x00000001;
53 const sal_uInt32 BIFF12_DEFNAME_FUNC
= 0x00000002;
54 const sal_uInt32 BIFF12_DEFNAME_VBNAME
= 0x00000004;
55 const sal_uInt32 BIFF12_DEFNAME_MACRO
= 0x00000008;
56 const sal_uInt32 BIFF12_DEFNAME_BUILTIN
= 0x00000020;
58 const sal_uInt16 BIFF_REFFLAG_COL1REL
= 0x0001;
59 const sal_uInt16 BIFF_REFFLAG_ROW1REL
= 0x0002;
60 const sal_uInt16 BIFF_REFFLAG_COL2REL
= 0x0004;
61 const sal_uInt16 BIFF_REFFLAG_ROW2REL
= 0x0008;
63 const sal_Char
* const spcOoxPrefix
= "_xlnm.";
65 const sal_Char
* const sppcBaseNames
[] =
83 OUString
lclGetBaseName( sal_Unicode cBuiltinId
)
85 OSL_ENSURE( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
), "lclGetBaseName - unsupported built-in identifier" );
86 OUStringBuffer aBuffer
;
87 if( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
) )
88 aBuffer
.appendAscii( sppcBaseNames
[ cBuiltinId
] );
90 aBuffer
.append( static_cast< sal_Int32
>( cBuiltinId
) );
91 return aBuffer
.makeStringAndClear();
94 OUString
lclGetPrefixedName( sal_Unicode cBuiltinId
)
96 return OUStringBuffer().appendAscii( spcOoxPrefix
).append( lclGetBaseName( cBuiltinId
) ).makeStringAndClear();
99 /** returns the built-in name identifier from a prefixed built-in name, e.g. '_xlnm.Print_Area'. */
100 sal_Unicode
lclGetBuiltinIdFromPrefixedName( const OUString
& rModelName
)
102 OUString aPrefix
= OUString::createFromAscii( spcOoxPrefix
);
103 sal_Int32 nPrefixLen
= aPrefix
.getLength();
104 if( rModelName
.matchIgnoreAsciiCase( aPrefix
) )
106 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
108 OUString aBaseName
= lclGetBaseName( cBuiltinId
);
109 sal_Int32 nBaseNameLen
= aBaseName
.getLength();
110 if( (rModelName
.getLength() == nPrefixLen
+ nBaseNameLen
) && rModelName
.matchIgnoreAsciiCase( aBaseName
, nPrefixLen
) )
114 return BIFF_DEFNAME_UNKNOWN
;
117 /** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */
118 sal_Unicode
lclGetBuiltinIdFromBaseName( const OUString
& rModelName
)
120 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
121 if( rModelName
.equalsIgnoreAsciiCaseAscii( sppcBaseNames
[ cBuiltinId
] ) )
123 return BIFF_DEFNAME_UNKNOWN
;
126 OUString
lclGetUpcaseModelName( const OUString
& rModelName
)
129 return rModelName
.toAsciiUpperCase();
132 void lclConvertRefFlags( sal_Int32
& ornFlags
, sal_Int32
& ornAbsPos
, sal_Int32
& ornRelPos
, sal_Int32 nBasePos
, sal_Int32 nApiRelFlag
, bool bRel
)
134 if( getFlag( ornFlags
, nApiRelFlag
) && !bRel
)
136 // convert relative to absolute
137 setFlag( ornFlags
, nApiRelFlag
, false );
138 ornAbsPos
= nBasePos
+ ornRelPos
;
140 else if( !getFlag( ornFlags
, nApiRelFlag
) && bRel
)
142 // convert absolute to relative
143 setFlag( ornFlags
, nApiRelFlag
, true );
144 ornRelPos
= ornAbsPos
- nBasePos
;
148 void lclConvertSingleRefFlags( SingleReference
& orApiRef
, const ScAddress
& rBaseAddr
, bool bColRel
, bool bRowRel
)
150 using namespace ::com::sun::star::sheet::ReferenceFlags
;
152 orApiRef
.Flags
, orApiRef
.Column
, orApiRef
.RelativeColumn
,
153 sal_Int32( rBaseAddr
.Col() ), COLUMN_RELATIVE
, bColRel
);
155 orApiRef
.Flags
, orApiRef
.Row
, orApiRef
.RelativeRow
,
156 rBaseAddr
.Row(), ROW_RELATIVE
, bRowRel
);
159 Any
lclConvertReference( const Any
& rRefAny
, const ScAddress
& rBaseAddr
, sal_uInt16 nRelFlags
)
161 if( rRefAny
.has
< SingleReference
>() && !getFlag( nRelFlags
, BIFF_REFFLAG_COL2REL
) && !getFlag( nRelFlags
, BIFF_REFFLAG_ROW2REL
) )
163 SingleReference aApiRef
;
165 lclConvertSingleRefFlags( aApiRef
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL1REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW1REL
) );
166 return Any( aApiRef
);
168 if( rRefAny
.has
< ComplexReference
>() )
170 ComplexReference aApiRef
;
172 lclConvertSingleRefFlags( aApiRef
.Reference1
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL1REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW1REL
) );
173 lclConvertSingleRefFlags( aApiRef
.Reference2
, rBaseAddr
, getFlag( nRelFlags
, BIFF_REFFLAG_COL2REL
), getFlag( nRelFlags
, BIFF_REFFLAG_ROW2REL
) );
174 return Any( aApiRef
);
181 DefinedNameModel::DefinedNameModel() :
191 DefinedNameBase::DefinedNameBase( const WorkbookHelper
& rHelper
) :
192 WorkbookHelper( rHelper
)
196 const OUString
& DefinedNameBase::getUpcaseModelName() const
198 if( maUpModelName
.isEmpty() )
199 maUpModelName
= lclGetUpcaseModelName( maModel
.maName
);
200 return maUpModelName
;
203 Any
DefinedNameBase::getReference( const ScAddress
& rBaseAddr
) const
205 if( maRefAny
.hasValue() && (maModel
.maName
.getLength() >= 2) && (maModel
.maName
[ 0 ] == '\x01') )
207 sal_Unicode cFlagsChar
= getUpcaseModelName()[ 1 ];
208 if( ('A' <= cFlagsChar
) && (cFlagsChar
<= 'P') )
210 sal_uInt16 nRelFlags
= static_cast< sal_uInt16
>( cFlagsChar
- 'A' );
211 if( maRefAny
.has
< ExternalReference
>() )
213 ExternalReference aApiExtRef
;
214 maRefAny
>>= aApiExtRef
;
215 Any aRefAny
= lclConvertReference( aApiExtRef
.Reference
, rBaseAddr
, nRelFlags
);
216 if( aRefAny
.hasValue() )
218 aApiExtRef
.Reference
<<= aRefAny
;
219 return Any( aApiExtRef
);
224 return lclConvertReference( maRefAny
, rBaseAddr
, nRelFlags
);
231 DefinedName::DefinedName( const WorkbookHelper
& rHelper
) :
232 DefinedNameBase( rHelper
),
233 mpScRangeData(nullptr),
236 mcBuiltinId( BIFF_DEFNAME_UNKNOWN
)
240 void DefinedName::importDefinedName( const AttributeList
& rAttribs
)
242 maModel
.maName
= rAttribs
.getXString( XML_name
, OUString() );
243 maModel
.mnSheet
= rAttribs
.getInteger( XML_localSheetId
, -1 );
244 maModel
.mnFuncGroupId
= rAttribs
.getInteger( XML_functionGroupId
, -1 );
245 maModel
.mbMacro
= rAttribs
.getBool( XML_xlm
, false );
246 maModel
.mbFunction
= rAttribs
.getBool( XML_function
, false );
247 maModel
.mbVBName
= rAttribs
.getBool( XML_vbProcedure
, false );
248 maModel
.mbHidden
= rAttribs
.getBool( XML_hidden
, false );
249 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
251 /* Detect built-in state from name itself, there is no built-in flag.
252 Built-in names are prefixed with '_xlnm.' instead. */
253 mcBuiltinId
= lclGetBuiltinIdFromPrefixedName( maModel
.maName
);
256 void DefinedName::setFormula( const OUString
& rFormula
)
258 maModel
.maFormula
= rFormula
;
261 void DefinedName::importDefinedName( SequenceInputStream
& rStrm
)
264 nFlags
= rStrm
.readuInt32();
265 rStrm
.skip( 1 ); // keyboard shortcut
266 maModel
.mnSheet
= rStrm
.readInt32();
267 rStrm
>> maModel
.maName
;
268 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
270 // macro function/command, hidden flag
271 maModel
.mnFuncGroupId
= extractValue
< sal_Int32
>( nFlags
, 6, 9 );
272 maModel
.mbMacro
= getFlag( nFlags
, BIFF12_DEFNAME_MACRO
);
273 maModel
.mbFunction
= getFlag( nFlags
, BIFF12_DEFNAME_FUNC
);
274 maModel
.mbVBName
= getFlag( nFlags
, BIFF12_DEFNAME_VBNAME
);
275 maModel
.mbHidden
= getFlag( nFlags
, BIFF12_DEFNAME_HIDDEN
);
277 // get built-in name index from name
278 if( getFlag( nFlags
, BIFF12_DEFNAME_BUILTIN
) )
279 mcBuiltinId
= lclGetBuiltinIdFromBaseName( maModel
.maName
);
281 // store token array data
282 sal_Int64 nRecPos
= rStrm
.tell();
283 sal_Int32 nFmlaSize
= rStrm
.readInt32();
284 rStrm
.skip( nFmlaSize
);
285 sal_Int32 nAddDataSize
= rStrm
.readInt32();
286 if( !rStrm
.isEof() && (nFmlaSize
> 0) && (nAddDataSize
>= 0) && (rStrm
.getRemaining() >= nAddDataSize
) )
288 sal_Int32 nTotalSize
= 8 + nFmlaSize
+ nAddDataSize
;
289 mxFormula
.reset( new StreamDataSequence
);
290 rStrm
.seek( nRecPos
);
291 rStrm
.readData( *mxFormula
, nTotalSize
);
295 void DefinedName::createNameObject( sal_Int32 nIndex
)
297 // do not create names for (macro) functions or VBA procedures
298 // #163146# do not ignore hidden names (may be regular names created by VBA scripts)
299 if( /*maModel.mbHidden ||*/ maModel
.mbFunction
|| maModel
.mbVBName
)
302 // skip BIFF names without stream position (e.g. BIFF3-BIFF4 internal 3D references)
303 if( (getFilterType() == FILTER_BIFF
) && !mxBiffStrm
.get() )
306 // convert original name to final Calc name (TODO: filter invalid characters from model name)
307 maCalcName
= isBuiltinName() ? lclGetPrefixedName( mcBuiltinId
) : maModel
.maName
;
309 // #163146# do not rename sheet-local names by default, this breaks VBA scripts
311 // special flags for this name
312 sal_Int32 nNameFlags
= 0;
313 using namespace ::com::sun::star::sheet::NamedRangeFlag
;
314 if( !isGlobalName() ) switch( mcBuiltinId
)
316 case BIFF_DEFNAME_CRITERIA
: nNameFlags
= FILTER_CRITERIA
; break;
317 case BIFF_DEFNAME_PRINTAREA
: nNameFlags
= PRINT_AREA
; break;
318 case BIFF_DEFNAME_PRINTTITLES
: nNameFlags
= COLUMN_HEADER
| ROW_HEADER
; break;
321 // create the name and insert it into the document, maCalcName will be changed to the resulting name
322 if (maModel
.mnSheet
>= 0)
323 mpScRangeData
= createLocalNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
, maModel
.mnSheet
);
325 mpScRangeData
= createNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
);
326 mnTokenIndex
= nIndex
;
329 std::unique_ptr
<ScTokenArray
> DefinedName::getScTokens(
330 const css::uno::Sequence
<css::sheet::ExternalLinkInfo
>& rExternalLinks
)
332 ScTokenArray aTokenArray
;
333 ScCompiler
aCompiler(&getScDocument(), ScAddress(0, 0, mnCalcSheet
));
334 aCompiler
.SetGrammar(formula::FormulaGrammar::GRAM_OOXML
);
335 aCompiler
.SetExternalLinks( rExternalLinks
);
336 std::unique_ptr
<ScTokenArray
> pArray(aCompiler
.CompileString(maModel
.maFormula
));
337 // Compile the tokens into RPN once to populate information into tokens
338 // where necessary, e.g. for TableRef inner reference. RPN can be discarded
339 // after, a resulting error must be reset.
340 sal_uInt16 nErr
= pArray
->GetCodeError();
341 aCompiler
.CompileTokenArray();
343 pArray
->SetCodeError(nErr
);
348 void DefinedName::convertFormula( const css::uno::Sequence
<css::sheet::ExternalLinkInfo
>& rExternalLinks
)
350 // macro function or vba procedure
354 // convert and set formula of the defined name
355 if ( getFilterType() == FILTER_OOXML
)
357 std::unique_ptr
<ScTokenArray
> pTokenArray
= getScTokens( rExternalLinks
);
358 mpScRangeData
->SetCode( *pTokenArray
);
361 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
362 Sequence
< FormulaToken
> aFTokenSeq
;
363 (void)ScTokenConversion::ConvertToTokenSequence( this->getScDocument(), aFTokenSeq
, *pTokenArray
);
364 // set built-in names (print ranges, repeated titles, filter ranges)
365 if( !isGlobalName() ) switch( mcBuiltinId
)
367 case BIFF_DEFNAME_PRINTAREA
:
369 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
370 ApiCellRangeList aPrintRanges
;
371 getFormulaParser().extractCellRangeList( aPrintRanges
, aFTokenSeq
, false, mnCalcSheet
);
372 if( xPrintAreas
.is() && !aPrintRanges
.empty() )
373 xPrintAreas
->setPrintAreas( aPrintRanges
.toSequence() );
376 case BIFF_DEFNAME_PRINTTITLES
:
378 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
379 ApiCellRangeList aTitleRanges
;
380 getFormulaParser().extractCellRangeList( aTitleRanges
, aFTokenSeq
, false, mnCalcSheet
);
381 if( xPrintAreas
.is() && !aTitleRanges
.empty() )
383 bool bHasRowTitles
= false;
384 bool bHasColTitles
= false;
385 const ScAddress
& rMaxPos
= getAddressConverter().getMaxAddress();
386 for( ::std::vector
< CellRangeAddress
>::const_iterator aIt
= aTitleRanges
.begin(), aEnd
= aTitleRanges
.end(); (aIt
!= aEnd
) && (!bHasRowTitles
|| !bHasColTitles
); ++aIt
)
388 bool bFullRow
= (aIt
->StartColumn
== 0) && ( aIt
->EndColumn
>= rMaxPos
.Col() );
389 bool bFullCol
= (aIt
->StartRow
== 0) && ( aIt
->EndRow
>= rMaxPos
.Row() );
390 if( !bHasRowTitles
&& bFullRow
&& !bFullCol
)
392 xPrintAreas
->setTitleRows( *aIt
);
393 xPrintAreas
->setPrintTitleRows( true );
394 bHasRowTitles
= true;
396 else if( !bHasColTitles
&& bFullCol
&& !bFullRow
)
398 xPrintAreas
->setTitleColumns( *aIt
);
399 xPrintAreas
->setPrintTitleColumns( true );
400 bHasColTitles
= true;
409 bool DefinedName::getAbsoluteRange( CellRangeAddress
& orRange
) const
411 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
412 Sequence
< FormulaToken
> aFTokenSeq
;
413 ScTokenConversion::ConvertToTokenSequence(getScDocument(), aFTokenSeq
, *pTokenArray
);
414 return getFormulaParser().extractCellRange( orRange
, aFTokenSeq
, false );
417 DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper
& rHelper
) :
418 WorkbookHelper( rHelper
)
422 DefinedNameRef
DefinedNamesBuffer::importDefinedName( const AttributeList
& rAttribs
)
424 DefinedNameRef xDefName
= createDefinedName();
425 xDefName
->importDefinedName( rAttribs
);
429 void DefinedNamesBuffer::importDefinedName( SequenceInputStream
& rStrm
)
431 createDefinedName()->importDefinedName( rStrm
);
434 void DefinedNamesBuffer::finalizeImport()
436 // first insert all names without formula definition into the document, and insert them into the maps
438 for( DefNameVector::iterator aIt
= maDefNames
.begin(), aEnd
= maDefNames
.end(); aIt
!= aEnd
; ++aIt
)
440 DefinedNameRef xDefName
= *aIt
;
441 xDefName
->createNameObject( ++index
);
442 // map by sheet index and original model name
443 maModelNameMap
[ SheetNameKey( xDefName
->getLocalCalcSheet(), xDefName
->getUpcaseModelName() ) ] = xDefName
;
444 // map by sheet index and built-in identifier
445 if( !xDefName
->isGlobalName() && xDefName
->isBuiltinName() )
446 maBuiltinMap
[ BuiltinKey( xDefName
->getLocalCalcSheet(), xDefName
->getBuiltinId() ) ] = xDefName
;
447 // map by API formula token identifier
448 sal_Int32 nTokenIndex
= xDefName
->getTokenIndex();
449 if( nTokenIndex
>= 0 )
450 maTokenIdMap
[ nTokenIndex
] = xDefName
;
453 /* Now convert all name formulas, so that the formula parser can find all
454 names in case of circular dependencies. */
455 maDefNames
.forEachMem( &DefinedName::convertFormula
, getExternalLinks().getLinkInfos());
458 DefinedNameRef
DefinedNamesBuffer::getByIndex( sal_Int32 nIndex
) const
460 return maDefNames
.get( nIndex
);
463 DefinedNameRef
DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex
) const
465 return maTokenIdMap
.get( nIndex
);
468 DefinedNameRef
DefinedNamesBuffer::getByModelName( const OUString
& rModelName
, sal_Int16 nCalcSheet
) const
470 OUString aUpcaseName
= lclGetUpcaseModelName( rModelName
);
471 DefinedNameRef xDefName
= maModelNameMap
.get( SheetNameKey( nCalcSheet
, aUpcaseName
) );
472 // lookup global name, if no local name exists
473 if( !xDefName
&& (nCalcSheet
>= 0) )
474 xDefName
= maModelNameMap
.get( SheetNameKey( -1, aUpcaseName
) );
478 DefinedNameRef
DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId
, sal_Int16 nCalcSheet
) const
480 return maBuiltinMap
.get( BuiltinKey( nCalcSheet
, cBuiltinId
) );
483 DefinedNameRef
DefinedNamesBuffer::createDefinedName()
485 DefinedNameRef
xDefName( new DefinedName( *this ) );
486 maDefNames
.push_back( xDefName
);
493 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */