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