Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / oox / defnamesbuffer.cxx
blob182be74080e18c6db5461b2edbbd0b5055304078
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 <memory>
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>
41 namespace oox {
42 namespace xls {
44 using namespace ::com::sun::star::sheet;
45 using namespace ::com::sun::star::table;
46 using namespace ::com::sun::star::uno;
48 namespace {
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[] =
60 "Consolidate_Area",
61 "Auto_Open",
62 "Auto_Close",
63 "Extract",
64 "Database",
65 "Criteria",
66 "Print_Area",
67 "Print_Titles",
68 "Recorder",
69 "Data_Form",
70 "Auto_Activate",
71 "Auto_Deactivate",
72 "Sheet_Title",
73 "_FilterDatabase"
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 ] );
82 else
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 ) )
102 return cBuiltinId;
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 ] ) )
113 return cBuiltinId;
114 return BIFF_DEFNAME_UNKNOWN;
117 OUString lclGetUpcaseModelName( const OUString& rModelName )
119 // TODO: i18n?
120 return rModelName.toAsciiUpperCase();
123 } // namespace
125 DefinedNameModel::DefinedNameModel() :
126 mnSheet( -1 ),
127 mnFuncGroupId( -1 ),
128 mbMacro( false ),
129 mbFunction( false ),
130 mbVBName( false ),
131 mbHidden( false )
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),
150 mnTokenIndex( -1 ),
151 mnCalcSheet( 0 ),
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 )
179 sal_uInt32 nFlags;
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 )
216 return;
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 );
236 else
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);
253 pArray->DelRPN();
254 pArray->SetCodeError(nErr);
256 return pArray;
259 void DefinedName::convertFormula( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks )
261 // macro function or vba procedure
262 if(!mpScRangeData)
263 return;
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) );
285 break;
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;
320 break;
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 );
342 return xDefName;
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
353 int index = 0;
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 ) );
390 return xDefName;
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 );
402 return xDefName;
405 } // namespace xls
406 } // namespace oox
408 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */