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 .
21 #include <defnamesbuffer.hxx>
23 #include <com/sun/star/sheet/NamedRangeFlag.hpp>
24 #include <com/sun/star/sheet/XPrintAreas.hpp>
25 #include <osl/diagnose.h>
26 #include <rtl/ustrbuf.hxx>
27 #include <oox/helper/binaryinputstream.hxx>
28 #include <oox/helper/attributelist.hxx>
29 #include <oox/token/tokens.hxx>
30 #include <addressconverter.hxx>
31 #include <biffhelper.hxx>
32 #include <externallinkbuffer.hxx>
33 #include <formulabase.hxx>
34 #include <formulaparser.hxx>
35 #include <worksheetbuffer.hxx>
36 #include <tokenarray.hxx>
37 #include <tokenuno.hxx>
38 #include <compiler.hxx>
39 #include <document.hxx>
44 using namespace ::com::sun::star::sheet
;
45 using namespace ::com::sun::star::table
;
46 using namespace ::com::sun::star::uno
;
50 const sal_uInt32 BIFF12_DEFNAME_HIDDEN
= 0x00000001;
51 const sal_uInt32 BIFF12_DEFNAME_FUNC
= 0x00000002;
52 const sal_uInt32 BIFF12_DEFNAME_VBNAME
= 0x00000004;
53 const sal_uInt32 BIFF12_DEFNAME_MACRO
= 0x00000008;
54 const sal_uInt32 BIFF12_DEFNAME_BUILTIN
= 0x00000020;
56 const OUStringLiteral
spcOoxPrefix("_xlnm.");
58 const sal_Char
* const sppcBaseNames
[] =
76 OUString
lclGetBaseName( sal_Unicode cBuiltinId
)
78 OSL_ENSURE( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
), "lclGetBaseName - unsupported built-in identifier" );
79 OUStringBuffer aBuffer
;
80 if( cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
) )
81 aBuffer
.appendAscii( sppcBaseNames
[ cBuiltinId
] );
83 aBuffer
.append( static_cast< sal_Int32
>( cBuiltinId
) );
84 return aBuffer
.makeStringAndClear();
87 OUString
lclGetPrefixedName( sal_Unicode cBuiltinId
)
89 return spcOoxPrefix
+ lclGetBaseName( cBuiltinId
);
92 /** returns the built-in name identifier from a prefixed built-in name, e.g. '_xlnm.Print_Area'. */
93 sal_Unicode
lclGetBuiltinIdFromPrefixedName( const OUString
& rModelName
)
95 if( rModelName
.matchIgnoreAsciiCase( spcOoxPrefix
) )
97 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
99 OUString aBaseName
= lclGetBaseName( cBuiltinId
);
100 sal_Int32 nBaseNameLen
= aBaseName
.getLength();
101 if( (rModelName
.getLength() == spcOoxPrefix
.size
+ nBaseNameLen
) && rModelName
.matchIgnoreAsciiCase( aBaseName
, spcOoxPrefix
.size
) )
105 return BIFF_DEFNAME_UNKNOWN
;
108 /** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */
109 sal_Unicode
lclGetBuiltinIdFromBaseName( const OUString
& rModelName
)
111 for( sal_Unicode cBuiltinId
= 0; cBuiltinId
< SAL_N_ELEMENTS( sppcBaseNames
); ++cBuiltinId
)
112 if( rModelName
.equalsIgnoreAsciiCaseAscii( sppcBaseNames
[ cBuiltinId
] ) )
114 return BIFF_DEFNAME_UNKNOWN
;
117 OUString
lclGetUpcaseModelName( const OUString
& rModelName
)
120 return rModelName
.toAsciiUpperCase();
125 DefinedNameModel::DefinedNameModel() :
135 DefinedNameBase::DefinedNameBase( const WorkbookHelper
& rHelper
) :
136 WorkbookHelper( rHelper
)
140 const OUString
& DefinedNameBase::getUpcaseModelName() const
142 if( maUpModelName
.isEmpty() )
143 maUpModelName
= lclGetUpcaseModelName( maModel
.maName
);
144 return maUpModelName
;
147 DefinedName::DefinedName( const WorkbookHelper
& rHelper
) :
148 DefinedNameBase( rHelper
),
149 mpScRangeData(nullptr),
152 mcBuiltinId( BIFF_DEFNAME_UNKNOWN
)
156 void DefinedName::importDefinedName( const AttributeList
& rAttribs
)
158 maModel
.maName
= rAttribs
.getXString( XML_name
, OUString() );
159 maModel
.mnSheet
= rAttribs
.getInteger( XML_localSheetId
, -1 );
160 maModel
.mnFuncGroupId
= rAttribs
.getInteger( XML_functionGroupId
, -1 );
161 maModel
.mbMacro
= rAttribs
.getBool( XML_xlm
, false );
162 maModel
.mbFunction
= rAttribs
.getBool( XML_function
, false );
163 maModel
.mbVBName
= rAttribs
.getBool( XML_vbProcedure
, false );
164 maModel
.mbHidden
= rAttribs
.getBool( XML_hidden
, false );
165 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
167 /* Detect built-in state from name itself, there is no built-in flag.
168 Built-in names are prefixed with '_xlnm.' instead. */
169 mcBuiltinId
= lclGetBuiltinIdFromPrefixedName( maModel
.maName
);
172 void DefinedName::setFormula( const OUString
& rFormula
)
174 maModel
.maFormula
= rFormula
;
177 void DefinedName::importDefinedName( SequenceInputStream
& rStrm
)
180 nFlags
= rStrm
.readuInt32();
181 rStrm
.skip( 1 ); // keyboard shortcut
182 maModel
.mnSheet
= rStrm
.readInt32();
183 rStrm
>> maModel
.maName
;
184 mnCalcSheet
= (maModel
.mnSheet
>= 0) ? getWorksheets().getCalcSheetIndex( maModel
.mnSheet
) : -1;
186 // macro function/command, hidden flag
187 maModel
.mnFuncGroupId
= extractValue
< sal_Int32
>( nFlags
, 6, 9 );
188 maModel
.mbMacro
= getFlag( nFlags
, BIFF12_DEFNAME_MACRO
);
189 maModel
.mbFunction
= getFlag( nFlags
, BIFF12_DEFNAME_FUNC
);
190 maModel
.mbVBName
= getFlag( nFlags
, BIFF12_DEFNAME_VBNAME
);
191 maModel
.mbHidden
= getFlag( nFlags
, BIFF12_DEFNAME_HIDDEN
);
193 // get built-in name index from name
194 if( getFlag( nFlags
, BIFF12_DEFNAME_BUILTIN
) )
195 mcBuiltinId
= lclGetBuiltinIdFromBaseName( maModel
.maName
);
197 // store token array data
198 sal_Int64 nRecPos
= rStrm
.tell();
199 sal_Int32 nFmlaSize
= rStrm
.readInt32();
200 rStrm
.skip( nFmlaSize
);
201 sal_Int32 nAddDataSize
= rStrm
.readInt32();
202 if( !rStrm
.isEof() && (nFmlaSize
> 0) && (nAddDataSize
>= 0) && (rStrm
.getRemaining() >= nAddDataSize
) )
204 sal_Int32 nTotalSize
= 8 + nFmlaSize
+ nAddDataSize
;
205 mxFormula
.reset( new StreamDataSequence
);
206 rStrm
.seek( nRecPos
);
207 rStrm
.readData( *mxFormula
, nTotalSize
);
211 void DefinedName::createNameObject( sal_Int32 nIndex
)
213 // do not create names for (macro) functions or VBA procedures
214 // #163146# do not ignore hidden names (may be regular names created by VBA scripts)
215 if( /*maModel.mbHidden ||*/ maModel
.mbFunction
|| maModel
.mbVBName
)
218 // convert original name to final Calc name (TODO: filter invalid characters from model name)
219 maCalcName
= isBuiltinName() ? lclGetPrefixedName( mcBuiltinId
) : maModel
.maName
;
221 // #163146# do not rename sheet-local names by default, this breaks VBA scripts
223 // special flags for this name
224 sal_Int32 nNameFlags
= 0;
225 using namespace ::com::sun::star::sheet::NamedRangeFlag
;
226 if( !isGlobalName() ) switch( mcBuiltinId
)
228 case BIFF_DEFNAME_CRITERIA
: nNameFlags
= FILTER_CRITERIA
; break;
229 case BIFF_DEFNAME_PRINTAREA
: nNameFlags
= PRINT_AREA
; break;
230 case BIFF_DEFNAME_PRINTTITLES
: nNameFlags
= COLUMN_HEADER
| ROW_HEADER
; break;
233 // create the name and insert it into the document, maCalcName will be changed to the resulting name
234 if (maModel
.mnSheet
>= 0)
235 mpScRangeData
= createLocalNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
, maModel
.mnSheet
);
237 mpScRangeData
= createNamedRangeObject( maCalcName
, ApiTokenSequence(), nIndex
, nNameFlags
);
238 mnTokenIndex
= nIndex
;
241 std::unique_ptr
<ScTokenArray
> DefinedName::getScTokens(
242 const css::uno::Sequence
<css::sheet::ExternalLinkInfo
>& rExternalLinks
)
244 ScCompiler
aCompiler(&getScDocument(), ScAddress(0, 0, mnCalcSheet
), formula::FormulaGrammar::GRAM_OOXML
);
245 aCompiler
.SetExternalLinks( rExternalLinks
);
246 std::unique_ptr
<ScTokenArray
> pArray(aCompiler
.CompileString(maModel
.maFormula
));
247 // Compile the tokens into RPN once to populate information into tokens
248 // where necessary, e.g. for TableRef inner reference. RPN can be discarded
249 // after, a resulting error must be reset.
250 FormulaError nErr
= pArray
->GetCodeError();
251 aCompiler
.CompileTokenArray();
252 getScDocument().CheckLinkFormulaNeedingCheck( *pArray
);
254 pArray
->SetCodeError(nErr
);
259 void DefinedName::convertFormula( const css::uno::Sequence
<css::sheet::ExternalLinkInfo
>& rExternalLinks
)
261 // macro function or vba procedure
265 // convert and set formula of the defined name
267 std::unique_ptr
<ScTokenArray
> pTokenArray
= getScTokens( rExternalLinks
);
268 mpScRangeData
->SetCode( *pTokenArray
);
271 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
272 Sequence
< FormulaToken
> aFTokenSeq
;
273 ScTokenConversion::ConvertToTokenSequence( getScDocument(), aFTokenSeq
, *pTokenArray
);
274 // set built-in names (print ranges, repeated titles, filter ranges)
275 if( !isGlobalName() ) switch( mcBuiltinId
)
277 case BIFF_DEFNAME_PRINTAREA
:
279 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
280 ScRangeList aPrintRanges
;
281 getFormulaParser().extractCellRangeList( aPrintRanges
, aFTokenSeq
, mnCalcSheet
);
282 if( xPrintAreas
.is() && !aPrintRanges
.empty() )
283 xPrintAreas
->setPrintAreas( AddressConverter::toApiSequence(aPrintRanges
) );
286 case BIFF_DEFNAME_PRINTTITLES
:
288 Reference
< XPrintAreas
> xPrintAreas( getSheetFromDoc( mnCalcSheet
), UNO_QUERY
);
289 ScRangeList aTitleRanges
;
290 getFormulaParser().extractCellRangeList( aTitleRanges
, aFTokenSeq
, mnCalcSheet
);
291 if( xPrintAreas
.is() && !aTitleRanges
.empty() )
293 bool bHasRowTitles
= false;
294 bool bHasColTitles
= false;
295 const ScAddress
& rMaxPos
= getAddressConverter().getMaxAddress();
296 for (size_t i
= 0, nSize
= aTitleRanges
.size(); i
< nSize
; ++i
)
298 const ScRange
& rRange
= aTitleRanges
[i
];
299 bool bFullRow
= (rRange
.aStart
.Col() == 0) && ( rRange
.aEnd
.Col() >= rMaxPos
.Col() );
300 bool bFullCol
= (rRange
.aStart
.Row() == 0) && ( rRange
.aEnd
.Row() >= rMaxPos
.Row() );
301 if( !bHasRowTitles
&& bFullRow
&& !bFullCol
)
303 xPrintAreas
->setTitleRows( CellRangeAddress(rRange
.aStart
.Tab(),
304 rRange
.aStart
.Col(), rRange
.aStart
.Row(),
305 rRange
.aEnd
.Col(), rRange
.aEnd
.Row()) );
306 xPrintAreas
->setPrintTitleRows( true );
307 bHasRowTitles
= true;
309 else if( !bHasColTitles
&& bFullCol
&& !bFullRow
)
311 xPrintAreas
->setTitleColumns( CellRangeAddress(rRange
.aStart
.Tab(),
312 rRange
.aStart
.Col(), rRange
.aStart
.Row(),
313 rRange
.aEnd
.Col(), rRange
.aEnd
.Row()) );
314 xPrintAreas
->setPrintTitleColumns( true );
315 bHasColTitles
= true;
324 bool DefinedName::getAbsoluteRange( ScRange
& orRange
) const
326 ScTokenArray
* pTokenArray
= mpScRangeData
->GetCode();
327 Sequence
< FormulaToken
> aFTokenSeq
;
328 ScTokenConversion::ConvertToTokenSequence(getScDocument(), aFTokenSeq
, *pTokenArray
);
329 return getFormulaParser().extractCellRange( orRange
, aFTokenSeq
);
333 DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper
& rHelper
) :
334 WorkbookHelper( rHelper
)
338 DefinedNameRef
DefinedNamesBuffer::importDefinedName( const AttributeList
& rAttribs
)
340 DefinedNameRef xDefName
= createDefinedName();
341 xDefName
->importDefinedName( rAttribs
);
345 void DefinedNamesBuffer::importDefinedName( SequenceInputStream
& rStrm
)
347 createDefinedName()->importDefinedName( rStrm
);
350 void DefinedNamesBuffer::finalizeImport()
352 // first insert all names without formula definition into the document, and insert them into the maps
354 for( DefinedNameRef
& xDefName
: maDefNames
)
356 xDefName
->createNameObject( ++index
);
357 // map by sheet index and original model name
358 maModelNameMap
[ SheetNameKey( xDefName
->getLocalCalcSheet(), xDefName
->getUpcaseModelName() ) ] = xDefName
;
359 // map by sheet index and built-in identifier
360 if( !xDefName
->isGlobalName() && xDefName
->isBuiltinName() )
361 maBuiltinMap
[ BuiltinKey( xDefName
->getLocalCalcSheet(), xDefName
->getBuiltinId() ) ] = xDefName
;
362 // map by API formula token identifier
363 sal_Int32 nTokenIndex
= xDefName
->getTokenIndex();
364 if( nTokenIndex
>= 0 )
365 maTokenIdMap
[ nTokenIndex
] = xDefName
;
368 /* Now convert all name formulas, so that the formula parser can find all
369 names in case of circular dependencies. */
370 maDefNames
.forEachMem( &DefinedName::convertFormula
, getExternalLinks().getLinkInfos());
373 DefinedNameRef
DefinedNamesBuffer::getByIndex( sal_Int32 nIndex
) const
375 return maDefNames
.get( nIndex
);
378 DefinedNameRef
DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex
) const
380 return maTokenIdMap
.get( nIndex
);
383 DefinedNameRef
DefinedNamesBuffer::getByModelName( const OUString
& rModelName
, sal_Int16 nCalcSheet
) const
385 OUString aUpcaseName
= lclGetUpcaseModelName( rModelName
);
386 DefinedNameRef xDefName
= maModelNameMap
.get( SheetNameKey( nCalcSheet
, aUpcaseName
) );
387 // lookup global name, if no local name exists
388 if( !xDefName
&& (nCalcSheet
>= 0) )
389 xDefName
= maModelNameMap
.get( SheetNameKey( -1, aUpcaseName
) );
393 DefinedNameRef
DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId
, sal_Int16 nCalcSheet
) const
395 return maBuiltinMap
.get( BuiltinKey( nCalcSheet
, cBuiltinId
) );
398 DefinedNameRef
DefinedNamesBuffer::createDefinedName()
400 DefinedNameRef
xDefName( new DefinedName( *this ) );
401 maDefNames
.push_back( xDefName
);
408 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */