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 <tokenuno.hxx>
23 #include <sal/macros.h>
24 #include <sal/log.hxx>
26 #include <com/sun/star/sheet/ComplexReference.hpp>
27 #include <com/sun/star/sheet/ExternalReference.hpp>
28 #include <com/sun/star/sheet/ReferenceFlags.hpp>
29 #include <com/sun/star/sheet/AddressConvention.hpp>
30 #include <com/sun/star/sheet/NameToken.hpp>
31 #include <com/sun/star/sheet/TableRefToken.hpp>
32 #include <com/sun/star/table/CellAddress.hpp>
34 #include <svl/itemprop.hxx>
35 #include <vcl/svapp.hxx>
36 #include <comphelper/string.hxx>
38 #include <miscuno.hxx>
39 #include <convuno.hxx>
40 #include <unonames.hxx>
41 #include <compiler.hxx>
42 #include <tokenarray.hxx>
44 #include <rangeseq.hxx>
45 #include <externalrefmgr.hxx>
47 using namespace ::formula
;
48 using namespace ::com::sun::star
;
50 static std::span
<const SfxItemPropertyMapEntry
> lcl_GetFormulaParserMap()
52 static const SfxItemPropertyMapEntry aFormulaParserMap_Impl
[] =
54 { SC_UNO_COMPILEFAP
, 0, cppu::UnoType
<bool>::get(), 0, 0 },
55 { SC_UNO_COMPILEENGLISH
, 0, cppu::UnoType
<bool>::get(), 0, 0 },
56 { SC_UNO_IGNORELEADING
, 0, cppu::UnoType
<bool>::get(), 0, 0 },
57 { SC_UNO_FORMULACONVENTION
, 0, cppu::UnoType
<decltype(sheet::AddressConvention::UNSPECIFIED
)>::get(), 0, 0 },
58 { SC_UNO_OPCODEMAP
, 0, cppu::UnoType
<uno::Sequence
< sheet::FormulaOpCodeMapEntry
>>::get(), 0, 0 },
60 return aFormulaParserMap_Impl
;
63 const formula::FormulaGrammar::AddressConvention aConvMap
[] = {
64 formula::FormulaGrammar::CONV_OOO
, // <- AddressConvention::OOO
65 formula::FormulaGrammar::CONV_XL_A1
, // <- AddressConvention::XL_A1
66 formula::FormulaGrammar::CONV_XL_R1C1
, // <- AddressConvention::XL_R1C1
67 formula::FormulaGrammar::CONV_XL_OOX
, // <- AddressConvention::XL_OOX
68 formula::FormulaGrammar::CONV_LOTUS_A1
// <- AddressConvention::LOTUS_A1
70 // sal_Int16 because of comparison of integer expressions below.
71 constexpr sal_Int16 nConvMapCount
= SAL_N_ELEMENTS(aConvMap
);
74 SC_SIMPLE_SERVICE_INFO( ScFormulaParserObj
, u
"ScFormulaParserObj"_ustr
, SC_SERVICENAME_FORMULAPARS
)
76 ScFormulaParserObj::ScFormulaParserObj(ScDocShell
* pDocSh
) :
78 mnConv( sheet::AddressConvention::UNSPECIFIED
),
80 mbIgnoreSpaces( true ),
81 mbCompileFAP( false ),
82 mbRefConventionChartOOXML( false )
84 mpDocShell
->GetDocument().AddUnoObject(*this);
87 ScFormulaParserObj::~ScFormulaParserObj()
92 mpDocShell
->GetDocument().RemoveUnoObject(*this);
95 void ScFormulaParserObj::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
97 if ( rHint
.GetId() == SfxHintId::Dying
)
103 void ScFormulaParserObj::SetCompilerFlags( ScCompiler
& rCompiler
) const
105 formula::FormulaGrammar::AddressConvention eConv
= formula::FormulaGrammar::CONV_UNSPECIFIED
;
106 if (mnConv
>= 0 && mnConv
< nConvMapCount
)
107 eConv
= aConvMap
[mnConv
];
109 // If mxOpCodeMap is not empty it overrides mbEnglish, and vice versa. We
110 // don't need to initialize things twice.
112 rCompiler
.SetFormulaLanguage( mxOpCodeMap
);
115 const sal_Int32 nFormulaLanguage
= (eConv
== formula::FormulaGrammar::CONV_XL_OOX
?
116 sheet::FormulaLanguage::OOXML
:
117 (mbEnglish
? sheet::FormulaLanguage::ENGLISH
: sheet::FormulaLanguage::NATIVE
));
118 ScCompiler::OpCodeMapPtr xMap
= rCompiler
.GetOpCodeMap( nFormulaLanguage
);
119 rCompiler
.SetFormulaLanguage( xMap
);
122 rCompiler
.SetRefConvention( eConv
);
123 rCompiler
.EnableJumpCommandReorder(!mbCompileFAP
);
124 rCompiler
.EnableStopOnError(!mbCompileFAP
);
126 rCompiler
.SetExternalLinks(maExternalLinks
);
127 rCompiler
.SetRefConventionChartOOXML(mbRefConventionChartOOXML
);
130 uno::Sequence
<sheet::FormulaToken
> SAL_CALL
ScFormulaParserObj::parseFormula(
131 const OUString
& aFormula
, const table::CellAddress
& rReferencePos
)
133 SolarMutexGuard aGuard
;
134 uno::Sequence
<sheet::FormulaToken
> aRet
;
138 ScDocument
& rDoc
= mpDocShell
->GetDocument();
139 ScExternalRefManager::ApiGuard
aExtRefGuard(rDoc
);
141 ScAddress
aRefPos( ScAddress::UNINITIALIZED
);
142 ScUnoConversion::FillScAddress( aRefPos
, rReferencePos
);
143 ScCompiler
aCompiler( rDoc
, aRefPos
, rDoc
.GetGrammar());
144 SetCompilerFlags( aCompiler
);
146 std::unique_ptr
<ScTokenArray
> pCode
= aCompiler
.CompileString( aFormula
);
147 if (pCode
->HasOpCode(ocTableRef
))
149 FormulaError nErr
= pCode
->GetCodeError();
150 aCompiler
.EnableJumpCommandReorder(true);
151 aCompiler
.CompileTokenArray(); // needed for corresponding inner reference
152 pCode
->DelRPN(); // can be discarded
153 pCode
->SetCodeError(nErr
); // reset to parsing error, if any
155 ScTokenConversion::ConvertToTokenSequence( rDoc
, aRet
, *pCode
);
161 OUString SAL_CALL
ScFormulaParserObj::printFormula(
162 const uno::Sequence
<sheet::FormulaToken
>& aTokens
, const table::CellAddress
& rReferencePos
)
164 SolarMutexGuard aGuard
;
169 ScDocument
& rDoc
= mpDocShell
->GetDocument();
170 ScTokenArray
aCode(rDoc
);
171 (void)ScTokenConversion::ConvertToTokenArray( rDoc
, aCode
, aTokens
);
172 ScAddress
aRefPos( ScAddress::UNINITIALIZED
);
173 ScUnoConversion::FillScAddress( aRefPos
, rReferencePos
);
174 ScCompiler
aCompiler(rDoc
, aRefPos
, aCode
, rDoc
.GetGrammar());
175 SetCompilerFlags( aCompiler
);
177 OUStringBuffer aBuffer
;
178 aCompiler
.CreateStringFromTokenArray( aBuffer
);
179 aRet
= aBuffer
.makeStringAndClear();
187 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
ScFormulaParserObj::getPropertySetInfo()
189 static uno::Reference
< beans::XPropertySetInfo
> aRef(new SfxItemPropertySetInfo( lcl_GetFormulaParserMap() ));
193 void SAL_CALL
ScFormulaParserObj::setPropertyValue(
194 const OUString
& aPropertyName
, const uno::Any
& aValue
)
196 SolarMutexGuard aGuard
;
197 if ( aPropertyName
== SC_UNO_COMPILEFAP
)
199 aValue
>>= mbCompileFAP
;
201 else if ( aPropertyName
== SC_UNO_COMPILEENGLISH
)
203 bool bOldEnglish
= mbEnglish
;
204 if (!(aValue
>>= mbEnglish
))
205 throw lang::IllegalArgumentException();
207 // Need to recreate the symbol map to change English property
208 // because the map is const. So for performance reasons set
209 // CompileEnglish _before_ OpCodeMap!
210 if (mxOpCodeMap
&& mbEnglish
!= bOldEnglish
)
212 mxOpCodeMap
= formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping
, mbEnglish
);
216 else if ( aPropertyName
== SC_UNO_FORMULACONVENTION
)
220 bool bOldEnglish
= mbEnglish
;
221 if (mnConv
>= 0 && mnConv
< nConvMapCount
222 && aConvMap
[mnConv
] == formula::FormulaGrammar::CONV_XL_OOX
)
225 // Same as for SC_UNO_COMPILEENGLISH, though an OpCodeMap should not
226 // had been set for CONV_XL_OOX.
227 if (mxOpCodeMap
&& mbEnglish
!= bOldEnglish
)
229 mxOpCodeMap
= formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping
, mbEnglish
);
232 else if ( aPropertyName
== SC_UNO_IGNORELEADING
)
234 aValue
>>= mbIgnoreSpaces
;
236 else if ( aPropertyName
== SC_UNO_OPCODEMAP
)
238 if (!(aValue
>>= maOpCodeMapping
))
239 throw lang::IllegalArgumentException();
241 mxOpCodeMap
= formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping
, mbEnglish
);
244 else if ( aPropertyName
== SC_UNO_EXTERNALLINKS
)
246 if (!(aValue
>>= maExternalLinks
))
247 throw lang::IllegalArgumentException();
249 else if ( aPropertyName
== SC_UNO_REF_CONV_CHARTOOXML
)
251 if (!(aValue
>>= mbRefConventionChartOOXML
))
252 throw lang::IllegalArgumentException();
255 throw beans::UnknownPropertyException(aPropertyName
);
258 uno::Any SAL_CALL
ScFormulaParserObj::getPropertyValue( const OUString
& aPropertyName
)
260 SolarMutexGuard aGuard
;
262 if ( aPropertyName
== SC_UNO_COMPILEFAP
)
264 aRet
<<= mbCompileFAP
;
266 else if ( aPropertyName
== SC_UNO_COMPILEENGLISH
)
270 else if ( aPropertyName
== SC_UNO_FORMULACONVENTION
)
274 else if ( aPropertyName
== SC_UNO_IGNORELEADING
)
276 aRet
<<= mbIgnoreSpaces
;
278 else if ( aPropertyName
== SC_UNO_OPCODEMAP
)
280 aRet
<<= maOpCodeMapping
;
282 else if ( aPropertyName
== SC_UNO_EXTERNALLINKS
)
284 aRet
<<= maExternalLinks
;
286 else if ( aPropertyName
== SC_UNO_REF_CONV_CHARTOOXML
)
288 aRet
<<= mbRefConventionChartOOXML
;
291 throw beans::UnknownPropertyException(aPropertyName
);
295 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFormulaParserObj
)
297 static void lcl_ExternalRefToApi( sheet::SingleReference
& rAPI
, const ScSingleRefData
& rRef
)
302 rAPI
.RelativeColumn
= 0;
303 rAPI
.RelativeRow
= 0;
304 rAPI
.RelativeSheet
= 0;
306 sal_Int32 nFlags
= 0;
307 if ( rRef
.IsColRel() )
309 nFlags
|= sheet::ReferenceFlags::COLUMN_RELATIVE
;
310 rAPI
.RelativeColumn
= rRef
.Col();
313 rAPI
.Column
= rRef
.Col();
315 if ( rRef
.IsRowRel() )
317 nFlags
|= sheet::ReferenceFlags::ROW_RELATIVE
;
318 rAPI
.RelativeRow
= rRef
.Row();
321 rAPI
.Row
= rRef
.Row();
323 if ( rRef
.IsColDeleted() ) nFlags
|= sheet::ReferenceFlags::COLUMN_DELETED
;
324 if ( rRef
.IsRowDeleted() ) nFlags
|= sheet::ReferenceFlags::ROW_DELETED
;
325 if ( rRef
.IsFlag3D() ) nFlags
|= sheet::ReferenceFlags::SHEET_3D
;
326 if ( rRef
.IsRelName() ) nFlags
|= sheet::ReferenceFlags::RELATIVE_NAME
;
330 static void lcl_SingleRefToApi( sheet::SingleReference
& rAPI
, const ScSingleRefData
& rRef
)
332 sal_Int32 nFlags
= 0;
333 if ( rRef
.IsColRel() )
335 nFlags
|= sheet::ReferenceFlags::COLUMN_RELATIVE
;
336 rAPI
.RelativeColumn
= rRef
.Col();
341 rAPI
.RelativeColumn
= 0;
342 rAPI
.Column
= rRef
.Col();
345 if ( rRef
.IsRowRel() )
347 nFlags
|= sheet::ReferenceFlags::ROW_RELATIVE
;
348 rAPI
.RelativeRow
= rRef
.Row();
353 rAPI
.RelativeRow
= 0;
354 rAPI
.Row
= rRef
.Row();
357 if ( rRef
.IsTabRel() )
359 nFlags
|= sheet::ReferenceFlags::SHEET_RELATIVE
;
360 rAPI
.RelativeSheet
= rRef
.Tab();
365 rAPI
.RelativeSheet
= 0;
366 rAPI
.Sheet
= rRef
.Tab();
369 if ( rRef
.IsColDeleted() ) nFlags
|= sheet::ReferenceFlags::COLUMN_DELETED
;
370 if ( rRef
.IsRowDeleted() ) nFlags
|= sheet::ReferenceFlags::ROW_DELETED
;
371 if ( rRef
.IsTabDeleted() ) nFlags
|= sheet::ReferenceFlags::SHEET_DELETED
;
372 if ( rRef
.IsFlag3D() ) nFlags
|= sheet::ReferenceFlags::SHEET_3D
;
373 if ( rRef
.IsRelName() ) nFlags
|= sheet::ReferenceFlags::RELATIVE_NAME
;
377 bool ScTokenConversion::ConvertToTokenArray( ScDocument
& rDoc
,
378 ScTokenArray
& rTokenArray
, const uno::Sequence
<sheet::FormulaToken
>& rSequence
)
380 return !rTokenArray
.Fill(rSequence
, rDoc
.GetSharedStringPool(), rDoc
.GetExternalRefManager());
383 void ScTokenConversion::ConvertToTokenSequence( const ScDocument
& rDoc
,
384 uno::Sequence
<sheet::FormulaToken
>& rSequence
, const ScTokenArray
& rTokenArray
,
385 bool bIgnoreTableRefNoInnerReference
)
387 sal_Int32 nLen
= static_cast<sal_Int32
>(rTokenArray
.GetLen());
388 formula::FormulaToken
** pTokens
= rTokenArray
.GetArray();
391 rSequence
.realloc(nLen
);
392 auto pSequence
= rSequence
.getArray();
393 for (sal_Int32 nPos
=0; nPos
<nLen
; nPos
++)
395 const formula::FormulaToken
& rToken
= *pTokens
[nPos
];
396 sheet::FormulaToken
& rAPI
= pSequence
[nPos
];
398 OpCode eOpCode
= rToken
.GetOpCode();
399 // eOpCode may be changed in the following switch/case
400 switch ( rToken
.GetType() )
403 // Only the count of spaces is stored as "long". Parameter count is ignored.
404 if ( eOpCode
== ocSpaces
)
405 rAPI
.Data
<<= static_cast<sal_Int32
>(rToken
.GetByte());
406 else if (eOpCode
== ocWhitespace
)
408 // Convention is one character repeated.
409 if (rToken
.GetByte() == 1)
410 rAPI
.Data
<<= OUString( rToken
.GetChar());
413 OUStringBuffer
aBuf( rToken
.GetByte());
414 comphelper::string::padToLength( aBuf
, rToken
.GetByte(), rToken
.GetChar());
415 rAPI
.Data
<<= aBuf
.makeStringAndClear();
419 rAPI
.Data
.clear(); // no data
421 case formula::svDouble
:
422 rAPI
.Data
<<= rToken
.GetDouble();
424 case formula::svString
:
425 rAPI
.Data
<<= rToken
.GetString().getString();
428 // Function name is stored as string.
429 // Byte (parameter count) is ignored.
430 rAPI
.Data
<<= rToken
.GetExternal();
434 sheet::SingleReference aSingleRef
;
435 lcl_SingleRefToApi( aSingleRef
, *rToken
.GetSingleRef() );
436 rAPI
.Data
<<= aSingleRef
;
439 case formula::svDoubleRef
:
441 sheet::ComplexReference aCompRef
;
442 lcl_SingleRefToApi( aCompRef
.Reference1
, *rToken
.GetSingleRef() );
443 lcl_SingleRefToApi( aCompRef
.Reference2
, *rToken
.GetSingleRef2() );
444 rAPI
.Data
<<= aCompRef
;
449 const ScTableRefToken
* pTR
;
450 if (rToken
.GetOpCode() == ocTableRef
&& (pTR
= dynamic_cast<const ScTableRefToken
*>(&rToken
)))
452 sheet::TableRefToken aTableRefToken
;
453 aTableRefToken
.Index
= static_cast<sal_Int32
>( pTR
->GetIndex());
454 aTableRefToken
.Item
= static_cast<sal_Int16
>( pTR
->GetItem());
455 const FormulaToken
* pRef
= pTR
->GetAreaRefRPN();
456 assert((pRef
|| bIgnoreTableRefNoInnerReference
)
457 && "something forgot to create RPN for ocTableRef inner reference");
458 (void)bIgnoreTableRefNoInnerReference
;
461 switch (pRef
->GetType())
464 lcl_SingleRefToApi( aTableRefToken
.Reference
.Reference1
, *pRef
->GetSingleRef());
465 aTableRefToken
.Reference
.Reference2
= aTableRefToken
.Reference
.Reference1
;
468 lcl_SingleRefToApi( aTableRefToken
.Reference
.Reference1
, *pRef
->GetSingleRef());
469 lcl_SingleRefToApi( aTableRefToken
.Reference
.Reference2
, *pRef
->GetSingleRef2());
475 rAPI
.Data
<<= aTableRefToken
;
479 sheet::NameToken aNameToken
;
480 aNameToken
.Index
= static_cast<sal_Int32
>( rToken
.GetIndex() );
481 aNameToken
.Sheet
= rToken
.GetSheet();
482 rAPI
.Data
<<= aNameToken
;
487 if (!ScRangeToSequence::FillMixedArray( rAPI
.Data
, rToken
.GetMatrix(), true))
490 case svExternalSingleRef
:
492 sheet::SingleReference aSingleRef
;
493 lcl_ExternalRefToApi( aSingleRef
, *rToken
.GetSingleRef() );
495 rDoc
.GetExternalRefManager()->getCacheTable(
496 rToken
.GetIndex(), rToken
.GetString().getString(), false, &nCacheId
);
497 aSingleRef
.Sheet
= static_cast< sal_Int32
>( nCacheId
);
498 sheet::ExternalReference aExtRef
;
499 aExtRef
.Index
= rToken
.GetIndex();
500 aExtRef
.Reference
<<= aSingleRef
;
501 rAPI
.Data
<<= aExtRef
;
505 case svExternalDoubleRef
:
507 sheet::ComplexReference aComplRef
;
508 lcl_ExternalRefToApi( aComplRef
.Reference1
, *rToken
.GetSingleRef() );
509 lcl_ExternalRefToApi( aComplRef
.Reference2
, *rToken
.GetSingleRef2() );
511 rDoc
.GetExternalRefManager()->getCacheTable(
512 rToken
.GetIndex(), rToken
.GetString().getString(), false, &nCacheId
);
513 aComplRef
.Reference1
.Sheet
= static_cast< sal_Int32
>( nCacheId
);
514 // NOTE: This assumes that cached sheets are in consecutive order!
515 aComplRef
.Reference2
.Sheet
=
516 aComplRef
.Reference1
.Sheet
+
517 (rToken
.GetSingleRef2()->Tab() - rToken
.GetSingleRef()->Tab());
518 sheet::ExternalReference aExtRef
;
519 aExtRef
.Index
= rToken
.GetIndex();
520 aExtRef
.Reference
<<= aComplRef
;
521 rAPI
.Data
<<= aExtRef
;
527 sheet::ExternalReference aExtRef
;
528 aExtRef
.Index
= rToken
.GetIndex();
529 aExtRef
.Reference
<<= rToken
.GetString().getString();
530 rAPI
.Data
<<= aExtRef
;
535 SAL_WARN("sc", "ScTokenConversion::ConvertToTokenSequence: unhandled token type " << StackVarEnumToString(rToken
.GetType()));
537 case svJump
: // occurs with ocIf, ocChoose
538 case svError
: // seems to be fairly common, and probably not exceptional and not worth a warning?
539 case svMissing
: // occurs with ocMissing
540 case svSep
: // occurs with ocSep, ocOpen, ocClose, ocArray*
541 rAPI
.Data
.clear(); // no data
543 rAPI
.OpCode
= static_cast<sal_Int32
>(eOpCode
); //! assuming equal values for the moment
547 rSequence
.realloc(0);
550 ScFormulaOpCodeMapperObj::ScFormulaOpCodeMapperObj(::std::unique_ptr
<formula::FormulaCompiler
> && _pCompiler
)
551 : formula::FormulaOpCodeMapperObj(std::move(_pCompiler
))
555 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */