1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <tokenuno.hxx>
22 
23 #include <sal/macros.h>
24 #include <sal/log.hxx>
25 
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/table/CellAddress.hpp>
32 
33 #include <svl/itemprop.hxx>
34 #include <vcl/svapp.hxx>
35 
36 #include <miscuno.hxx>
37 #include <convuno.hxx>
38 #include <unonames.hxx>
39 #include <compiler.hxx>
40 #include <tokenarray.hxx>
41 #include <docsh.hxx>
42 #include <rangeseq.hxx>
43 #include <externalrefmgr.hxx>
44 
45 using namespace ::formula;
46 using namespace ::com::sun::star;
47 
lcl_GetFormulaParserMap()48 static const SfxItemPropertyMapEntry* lcl_GetFormulaParserMap()
49 {
50     static const SfxItemPropertyMapEntry aFormulaParserMap_Impl[] =
51     {
52         {u"" SC_UNO_COMPILEFAP,           0,  cppu::UnoType<bool>::get(),                   0, 0 },
53         {u"" SC_UNO_COMPILEENGLISH,       0,  cppu::UnoType<bool>::get(),                   0, 0 },
54         {u"" SC_UNO_IGNORELEADING,        0,  cppu::UnoType<bool>::get(),                   0, 0 },
55         {u"" SC_UNO_FORMULACONVENTION,    0,  cppu::UnoType<decltype(sheet::AddressConvention::UNSPECIFIED)>::get(), 0, 0 },
56         {u"" SC_UNO_OPCODEMAP,            0,  cppu::UnoType<uno::Sequence< sheet::FormulaOpCodeMapEntry >>::get(), 0, 0 },
57         { u"", 0, css::uno::Type(), 0, 0 }
58     };
59     return aFormulaParserMap_Impl;
60 }
61 
62 const formula::FormulaGrammar::AddressConvention aConvMap[] = {
63     formula::FormulaGrammar::CONV_OOO,        // <- AddressConvention::OOO
64     formula::FormulaGrammar::CONV_XL_A1,      // <- AddressConvention::XL_A1
65     formula::FormulaGrammar::CONV_XL_R1C1,    // <- AddressConvention::XL_R1C1
66     formula::FormulaGrammar::CONV_XL_OOX,     // <- AddressConvention::XL_OOX
67     formula::FormulaGrammar::CONV_LOTUS_A1    // <- AddressConvention::LOTUS_A1
68 };
69 // sal_Int16 because of comparison of integer expressions below.
70 constexpr sal_Int16 nConvMapCount = SAL_N_ELEMENTS(aConvMap);
71 
72 
73 SC_SIMPLE_SERVICE_INFO( ScFormulaParserObj, "ScFormulaParserObj", SC_SERVICENAME_FORMULAPARS )
74 
ScFormulaParserObj(ScDocShell * pDocSh)75 ScFormulaParserObj::ScFormulaParserObj(ScDocShell* pDocSh) :
76     mpDocShell( pDocSh ),
77     mnConv( sheet::AddressConvention::UNSPECIFIED ),
78     mbEnglish( false ),
79     mbIgnoreSpaces( true ),
80     mbCompileFAP( false )
81 {
82     mpDocShell->GetDocument().AddUnoObject(*this);
83 }
84 
~ScFormulaParserObj()85 ScFormulaParserObj::~ScFormulaParserObj()
86 {
87     SolarMutexGuard g;
88 
89     if (mpDocShell)
90         mpDocShell->GetDocument().RemoveUnoObject(*this);
91 }
92 
Notify(SfxBroadcaster &,const SfxHint & rHint)93 void ScFormulaParserObj::Notify( SfxBroadcaster&, const SfxHint& rHint )
94 {
95     if ( rHint.GetId() == SfxHintId::Dying )
96         mpDocShell = nullptr;
97 }
98 
99 // XFormulaParser
100 
SetCompilerFlags(ScCompiler & rCompiler) const101 void ScFormulaParserObj::SetCompilerFlags( ScCompiler& rCompiler ) const
102 {
103     formula::FormulaGrammar::AddressConvention eConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
104     if (mnConv >= 0 && mnConv < nConvMapCount)
105         eConv = aConvMap[mnConv];
106 
107     // If mxOpCodeMap is not empty it overrides mbEnglish, and vice versa. We
108     // don't need to initialize things twice.
109     if (mxOpCodeMap)
110         rCompiler.SetFormulaLanguage( mxOpCodeMap );
111     else
112     {
113         const sal_Int32 nFormulaLanguage = (eConv == formula::FormulaGrammar::CONV_XL_OOX ?
114                 sheet::FormulaLanguage::OOXML :
115                 (mbEnglish ? sheet::FormulaLanguage::ENGLISH : sheet::FormulaLanguage::NATIVE));
116         ScCompiler::OpCodeMapPtr xMap = rCompiler.GetOpCodeMap( nFormulaLanguage);
117         rCompiler.SetFormulaLanguage( xMap);
118     }
119 
120     rCompiler.SetRefConvention( eConv );
121     rCompiler.EnableJumpCommandReorder(!mbCompileFAP);
122     rCompiler.EnableStopOnError(!mbCompileFAP);
123 
124     rCompiler.SetExternalLinks( maExternalLinks);
125 }
126 
parseFormula(const OUString & aFormula,const table::CellAddress & rReferencePos)127 uno::Sequence<sheet::FormulaToken> SAL_CALL ScFormulaParserObj::parseFormula(
128     const OUString& aFormula, const table::CellAddress& rReferencePos )
129 {
130     SolarMutexGuard aGuard;
131     uno::Sequence<sheet::FormulaToken> aRet;
132 
133     if (mpDocShell)
134     {
135         ScDocument& rDoc = mpDocShell->GetDocument();
136         ScExternalRefManager::ApiGuard aExtRefGuard(rDoc);
137 
138         ScAddress aRefPos( ScAddress::UNINITIALIZED );
139         ScUnoConversion::FillScAddress( aRefPos, rReferencePos );
140         ScCompiler aCompiler( rDoc, aRefPos, rDoc.GetGrammar());
141         SetCompilerFlags( aCompiler );
142 
143         std::unique_ptr<ScTokenArray> pCode = aCompiler.CompileString( aFormula );
144         ScTokenConversion::ConvertToTokenSequence( rDoc, aRet, *pCode );
145     }
146 
147     return aRet;
148 }
149 
printFormula(const uno::Sequence<sheet::FormulaToken> & aTokens,const table::CellAddress & rReferencePos)150 OUString SAL_CALL ScFormulaParserObj::printFormula(
151         const uno::Sequence<sheet::FormulaToken>& aTokens, const table::CellAddress& rReferencePos )
152 {
153     SolarMutexGuard aGuard;
154     OUString aRet;
155 
156     if (mpDocShell)
157     {
158         ScDocument& rDoc = mpDocShell->GetDocument();
159         ScTokenArray aCode(rDoc);
160         (void)ScTokenConversion::ConvertToTokenArray( rDoc, aCode, aTokens );
161         ScAddress aRefPos( ScAddress::UNINITIALIZED );
162         ScUnoConversion::FillScAddress( aRefPos, rReferencePos );
163         ScCompiler aCompiler(rDoc, aRefPos, aCode, rDoc.GetGrammar());
164         SetCompilerFlags( aCompiler );
165 
166         OUStringBuffer aBuffer;
167         aCompiler.CreateStringFromTokenArray( aBuffer );
168         aRet = aBuffer.makeStringAndClear();
169     }
170 
171     return aRet;
172 }
173 
174 // XPropertySet
175 
getPropertySetInfo()176 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFormulaParserObj::getPropertySetInfo()
177 {
178     SolarMutexGuard aGuard;
179     static uno::Reference< beans::XPropertySetInfo > aRef(new SfxItemPropertySetInfo( lcl_GetFormulaParserMap() ));
180     return aRef;
181 }
182 
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)183 void SAL_CALL ScFormulaParserObj::setPropertyValue(
184                         const OUString& aPropertyName, const uno::Any& aValue )
185 {
186     SolarMutexGuard aGuard;
187     if ( aPropertyName == SC_UNO_COMPILEFAP )
188     {
189         aValue >>= mbCompileFAP;
190     }
191     else if ( aPropertyName == SC_UNO_COMPILEENGLISH )
192     {
193         bool bOldEnglish = mbEnglish;
194         if (!(aValue >>= mbEnglish))
195             throw lang::IllegalArgumentException();
196 
197         // Need to recreate the symbol map to change English property
198         // because the map is const. So for performance reasons set
199         // CompileEnglish _before_ OpCodeMap!
200         if (mxOpCodeMap && mbEnglish != bOldEnglish)
201         {
202             ScDocument& rDoc = mpDocShell->GetDocument();
203             ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
204             mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
205         }
206 
207     }
208     else if ( aPropertyName == SC_UNO_FORMULACONVENTION )
209     {
210         aValue >>= mnConv;
211 
212         bool bOldEnglish = mbEnglish;
213         if (mnConv >= 0 && mnConv < nConvMapCount
214                 && aConvMap[mnConv] == formula::FormulaGrammar::CONV_XL_OOX)
215             mbEnglish = true;
216 
217         // Same as for SC_UNO_COMPILEENGLISH, though an OpCodeMap should not
218         // had been set for CONV_XL_OOX.
219         if (mxOpCodeMap && mbEnglish != bOldEnglish)
220         {
221             ScDocument& rDoc = mpDocShell->GetDocument();
222             ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
223             mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
224         }
225     }
226     else if ( aPropertyName == SC_UNO_IGNORELEADING )
227     {
228         aValue >>= mbIgnoreSpaces;
229     }
230     else if ( aPropertyName == SC_UNO_OPCODEMAP )
231     {
232         if (!(aValue >>= maOpCodeMapping))
233             throw lang::IllegalArgumentException();
234 
235         ScDocument& rDoc = mpDocShell->GetDocument();
236         ScCompiler aCompiler(rDoc, ScAddress(), rDoc.GetGrammar());
237         mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( maOpCodeMapping, mbEnglish);
238 
239     }
240     else if ( aPropertyName == SC_UNO_EXTERNALLINKS )
241     {
242         if (!(aValue >>= maExternalLinks))
243             throw lang::IllegalArgumentException();
244     }
245     else
246         throw beans::UnknownPropertyException(aPropertyName);
247 }
248 
getPropertyValue(const OUString & aPropertyName)249 uno::Any SAL_CALL ScFormulaParserObj::getPropertyValue( const OUString& aPropertyName )
250 {
251     SolarMutexGuard aGuard;
252     uno::Any aRet;
253     if ( aPropertyName == SC_UNO_COMPILEFAP )
254     {
255         aRet <<= mbCompileFAP;
256     }
257     else if ( aPropertyName == SC_UNO_COMPILEENGLISH )
258     {
259         aRet <<= mbEnglish;
260     }
261     else if ( aPropertyName == SC_UNO_FORMULACONVENTION )
262     {
263         aRet <<= mnConv;
264     }
265     else if ( aPropertyName == SC_UNO_IGNORELEADING )
266     {
267         aRet <<= mbIgnoreSpaces;
268     }
269     else if ( aPropertyName == SC_UNO_OPCODEMAP )
270     {
271         aRet <<= maOpCodeMapping;
272     }
273     else if ( aPropertyName == SC_UNO_EXTERNALLINKS )
274     {
275         aRet <<= maExternalLinks;
276     }
277     else
278         throw beans::UnknownPropertyException(aPropertyName);
279     return aRet;
280 }
281 
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScFormulaParserObj)282 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFormulaParserObj )
283 
284 static void lcl_ExternalRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef )
285 {
286     rAPI.Column         = 0;
287     rAPI.Row            = 0;
288     rAPI.Sheet          = 0;
289     rAPI.RelativeColumn = 0;
290     rAPI.RelativeRow    = 0;
291     rAPI.RelativeSheet  = 0;
292 
293     sal_Int32 nFlags = 0;
294     if ( rRef.IsColRel() )
295     {
296         nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE;
297         rAPI.RelativeColumn = rRef.Col();
298     }
299     else
300         rAPI.Column = rRef.Col();
301 
302     if ( rRef.IsRowRel() )
303     {
304         nFlags |= sheet::ReferenceFlags::ROW_RELATIVE;
305         rAPI.RelativeRow = rRef.Row();
306     }
307     else
308         rAPI.Row = rRef.Row();
309 
310     if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED;
311     if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED;
312     if ( rRef.IsFlag3D() )     nFlags |= sheet::ReferenceFlags::SHEET_3D;
313     if ( rRef.IsRelName() )    nFlags |= sheet::ReferenceFlags::RELATIVE_NAME;
314     rAPI.Flags = nFlags;
315 }
316 
lcl_SingleRefToApi(sheet::SingleReference & rAPI,const ScSingleRefData & rRef)317 static void lcl_SingleRefToApi( sheet::SingleReference& rAPI, const ScSingleRefData& rRef )
318 {
319     sal_Int32 nFlags = 0;
320     if ( rRef.IsColRel() )
321     {
322         nFlags |= sheet::ReferenceFlags::COLUMN_RELATIVE;
323         rAPI.RelativeColumn = rRef.Col();
324         rAPI.Column = 0;
325     }
326     else
327     {
328         rAPI.RelativeColumn = 0;
329         rAPI.Column = rRef.Col();
330     }
331 
332     if ( rRef.IsRowRel() )
333     {
334         nFlags |= sheet::ReferenceFlags::ROW_RELATIVE;
335         rAPI.RelativeRow = rRef.Row();
336         rAPI.Row = 0;
337     }
338     else
339     {
340         rAPI.RelativeRow = 0;
341         rAPI.Row = rRef.Row();
342     }
343 
344     if ( rRef.IsTabRel() )
345     {
346         nFlags |= sheet::ReferenceFlags::SHEET_RELATIVE;
347         rAPI.RelativeSheet = rRef.Tab();
348         rAPI.Sheet = 0;
349     }
350     else
351     {
352         rAPI.RelativeSheet = 0;
353         rAPI.Sheet = rRef.Tab();
354     }
355 
356     if ( rRef.IsColDeleted() ) nFlags |= sheet::ReferenceFlags::COLUMN_DELETED;
357     if ( rRef.IsRowDeleted() ) nFlags |= sheet::ReferenceFlags::ROW_DELETED;
358     if ( rRef.IsTabDeleted() ) nFlags |= sheet::ReferenceFlags::SHEET_DELETED;
359     if ( rRef.IsFlag3D() )     nFlags |= sheet::ReferenceFlags::SHEET_3D;
360     if ( rRef.IsRelName() )    nFlags |= sheet::ReferenceFlags::RELATIVE_NAME;
361     rAPI.Flags = nFlags;
362 }
363 
ConvertToTokenArray(ScDocument & rDoc,ScTokenArray & rTokenArray,const uno::Sequence<sheet::FormulaToken> & rSequence)364 bool ScTokenConversion::ConvertToTokenArray( ScDocument& rDoc,
365         ScTokenArray& rTokenArray, const uno::Sequence<sheet::FormulaToken>& rSequence )
366 {
367     return !rTokenArray.Fill(rSequence, rDoc.GetSharedStringPool(), rDoc.GetExternalRefManager());
368 }
369 
ConvertToTokenSequence(const ScDocument & rDoc,uno::Sequence<sheet::FormulaToken> & rSequence,const ScTokenArray & rTokenArray)370 void ScTokenConversion::ConvertToTokenSequence( const ScDocument& rDoc,
371         uno::Sequence<sheet::FormulaToken>& rSequence, const ScTokenArray& rTokenArray )
372 {
373     sal_Int32 nLen = static_cast<sal_Int32>(rTokenArray.GetLen());
374     formula::FormulaToken** pTokens = rTokenArray.GetArray();
375     if ( pTokens )
376     {
377         rSequence.realloc(nLen);
378         for (sal_Int32 nPos=0; nPos<nLen; nPos++)
379         {
380             const formula::FormulaToken& rToken = *pTokens[nPos];
381             sheet::FormulaToken& rAPI = rSequence[nPos];
382 
383             OpCode eOpCode = rToken.GetOpCode();
384             // eOpCode may be changed in the following switch/case
385             switch ( rToken.GetType() )
386             {
387                 case svByte:
388                     // Only the count of spaces is stored as "long". Parameter count is ignored.
389                     if ( eOpCode == ocSpaces )
390                         rAPI.Data <<= static_cast<sal_Int32>(rToken.GetByte());
391                     else
392                         rAPI.Data.clear();      // no data
393                     break;
394                 case formula::svDouble:
395                     rAPI.Data <<= rToken.GetDouble();
396                     break;
397                 case formula::svString:
398                     rAPI.Data <<= rToken.GetString().getString();
399                     break;
400                 case svExternal:
401                     // Function name is stored as string.
402                     // Byte (parameter count) is ignored.
403                     rAPI.Data <<= rToken.GetExternal();
404                     break;
405                 case svSingleRef:
406                     {
407                         sheet::SingleReference aSingleRef;
408                         lcl_SingleRefToApi( aSingleRef, *rToken.GetSingleRef() );
409                         rAPI.Data <<= aSingleRef;
410                     }
411                     break;
412                 case formula::svDoubleRef:
413                     {
414                         sheet::ComplexReference aCompRef;
415                         lcl_SingleRefToApi( aCompRef.Reference1, *rToken.GetSingleRef() );
416                         lcl_SingleRefToApi( aCompRef.Reference2, *rToken.GetSingleRef2() );
417                         rAPI.Data <<= aCompRef;
418                     }
419                     break;
420                 case svIndex:
421                     {
422                         sheet::NameToken aNameToken;
423                         aNameToken.Index = static_cast<sal_Int32>( rToken.GetIndex() );
424                         aNameToken.Sheet = rToken.GetSheet();
425                         rAPI.Data <<= aNameToken;
426                     }
427                     break;
428                 case svMatrix:
429                     if (!ScRangeToSequence::FillMixedArray( rAPI.Data, rToken.GetMatrix(), true))
430                         rAPI.Data.clear();
431                     break;
432                 case svExternalSingleRef:
433                     {
434                         sheet::SingleReference aSingleRef;
435                         lcl_ExternalRefToApi( aSingleRef, *rToken.GetSingleRef() );
436                         size_t nCacheId;
437                         rDoc.GetExternalRefManager()->getCacheTable(
438                             rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId);
439                         aSingleRef.Sheet = static_cast< sal_Int32 >( nCacheId );
440                         sheet::ExternalReference aExtRef;
441                         aExtRef.Index = rToken.GetIndex();
442                         aExtRef.Reference <<= aSingleRef;
443                         rAPI.Data <<= aExtRef;
444                         eOpCode = ocPush;
445                     }
446                     break;
447                 case svExternalDoubleRef:
448                     {
449                         sheet::ComplexReference aComplRef;
450                         lcl_ExternalRefToApi( aComplRef.Reference1, *rToken.GetSingleRef() );
451                         lcl_ExternalRefToApi( aComplRef.Reference2, *rToken.GetSingleRef2() );
452                         size_t nCacheId;
453                         rDoc.GetExternalRefManager()->getCacheTable(
454                             rToken.GetIndex(), rToken.GetString().getString(), false, &nCacheId);
455                         aComplRef.Reference1.Sheet = static_cast< sal_Int32 >( nCacheId );
456                         // NOTE: This assumes that cached sheets are in consecutive order!
457                         aComplRef.Reference2.Sheet =
458                             aComplRef.Reference1.Sheet +
459                             (rToken.GetSingleRef2()->Tab() - rToken.GetSingleRef()->Tab());
460                         sheet::ExternalReference aExtRef;
461                         aExtRef.Index = rToken.GetIndex();
462                         aExtRef.Reference <<= aComplRef;
463                         rAPI.Data <<= aExtRef;
464                         eOpCode = ocPush;
465                     }
466                     break;
467                 case svExternalName:
468                     {
469                         sheet::ExternalReference aExtRef;
470                         aExtRef.Index = rToken.GetIndex();
471                         aExtRef.Reference <<= rToken.GetString().getString();
472                         rAPI.Data <<= aExtRef;
473                         eOpCode = ocPush;
474                     }
475                     break;
476                 default:
477                     SAL_WARN("sc",  "ScTokenConversion::ConvertToTokenSequence: unhandled token type " << StackVarEnumToString(rToken.GetType()));
478                     [[fallthrough]];
479                 case svJump:    // occurs with ocIf, ocChoose
480                 case svError:   // seems to be fairly common, and probably not exceptional and not worth a warning?
481                 case svMissing: // occurs with ocMissing
482                 case svSep:     // occurs with ocSep, ocOpen, ocClose, ocArray*
483                     rAPI.Data.clear();      // no data
484             }
485             rAPI.OpCode = static_cast<sal_Int32>(eOpCode);      //! assuming equal values for the moment
486         }
487     }
488     else
489         rSequence.realloc(0);
490 }
491 
ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> && _pCompiler)492 ScFormulaOpCodeMapperObj::ScFormulaOpCodeMapperObj(::std::unique_ptr<formula::FormulaCompiler> && _pCompiler)
493 : formula::FormulaOpCodeMapperObj(std::move(_pCompiler))
494 {
495 }
496 
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
498