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