LibreOffice Module sc (master) 1
formularesult.cxx
Go to the documentation of this file.
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
10#include <formularesult.hxx>
11#include <scmatrix.hxx>
12#include <token.hxx>
13
14#include <sal/log.hxx>
15#include <utility>
16
17namespace sc {
18
20FormulaResultValue::FormulaResultValue( double fValue ) : mfValue(fValue), meType(Value), mnError(FormulaError::NONE) {}
21FormulaResultValue::FormulaResultValue( svl::SharedString aStr, bool bMultiLine ) : mfValue(0.0), maString(std::move(aStr)), mbMultiLine(bMultiLine), meType(String), mnError(FormulaError::NONE) {}
22FormulaResultValue::FormulaResultValue( FormulaError nErr ) : mfValue(0.0), meType(Error), mnError(nErr) {}
23
24}
25
27 mpToken(nullptr),
28 mbToken(true),
29 mbEmpty(false),
30 mbEmptyDisplayedAsString(false),
31 mbValueCached(false),
32 meMultiline(MULTILINE_UNKNOWN),
33 mnError(FormulaError::NONE) {}
34
36 mbToken( r.mbToken),
37 mbEmpty( r.mbEmpty),
38 mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString),
39 mbValueCached( r.mbValueCached),
40 meMultiline( r.meMultiline),
41 mnError( r.mnError)
42{
43 if (mbToken)
44 {
45 mpToken = r.mpToken;
46 if (mpToken)
47 {
48 // Since matrix dimension and
49 // results are assigned to a matrix
50 // cell formula token we have to
51 // clone that instead of sharing it.
52 const ScMatrixFormulaCellToken* pMatFormula =
54 if (pMatFormula)
55 mpToken = new ScMatrixFormulaCellToken( *pMatFormula);
56 mpToken->IncRef();
57 }
58 }
59 else
60 mfValue = r.mfValue;
61}
62
64 mbToken(false),
65 mbEmpty(false),
66 mbEmptyDisplayedAsString(false),
67 mbValueCached(false),
68 meMultiline(MULTILINE_UNKNOWN),
69 mnError(FormulaError::NONE)
70{
71 SetToken( p);
72}
73
75{
76 if (mbToken && mpToken)
77 mpToken->DecRef();
78}
79
81{
82 mnError = FormulaError::NONE;
83 mbEmpty = false;
86 mbValueCached = false;
87}
88
90{
92 if (!p)
93 {
94 mpToken = p;
95 mbToken = true;
96 }
97 else
98 {
99 switch (p->GetType())
100 {
101 case formula::svError:
102 mnError = p->GetError();
103 p->DecRef();
104 mbToken = false;
105 // set in case mnError is 0 now, which shouldn't happen but ...
106 mfValue = 0.0;
108 break;
110 mbEmpty = true;
111 mbEmptyDisplayedAsString = static_cast<const ScEmptyCellToken*>(p)->IsDisplayedAsString();
112 p->DecRef();
113 mbToken = false;
115 // Take advantage of fast double result return for empty result token.
116 // by setting mfValue to 0 and turning on mbValueCached flag.
117 mfValue = 0.0;
118 mbValueCached = true;
119 break;
121 mfValue = p->GetDouble();
122 p->DecRef();
123 mbToken = false;
125 mbValueCached = true;
126 break;
127 default:
128 mpToken = p;
129 mbToken = true;
130 }
131 }
132}
133
135{
136 Assign( r);
137 return *this;
138}
139
141{
142 if (this == &r)
143 return;
144
145 // It is important to reset the value-cache flag to that of the source
146 // unconditionally.
148
149 if (r.mbEmpty)
150 {
151 if (mbToken && mpToken)
152 mpToken->DecRef();
153 mbToken = false;
154 mbEmpty = true;
157 // here r.mfValue will be 0.0 which is ensured in ResolveToken().
158 mfValue = 0.0;
159 }
160 else if (r.mbToken)
161 {
162 // Matrix formula cell token must be cloned, see copy-ctor.
163 const ScMatrixFormulaCellToken* pMatFormula =
165 if (pMatFormula)
166 SetToken( new ScMatrixFormulaCellToken( *pMatFormula));
167 else
168 SetToken( r.mpToken);
169 }
170 else
171 SetDouble( r.mfValue);
172 // If there was an error there will be an error, no matter what Set...()
173 // methods did.
175}
176
178{
180 if (p)
181 p->IncRef();
182 // Handle a result obtained from the interpreter to be assigned to a matrix
183 // formula cell's ScMatrixFormulaCellToken.
185 if (pMatFormula)
186 {
187 const ScMatrixCellResultToken* pMatResult =
188 (p && p->GetType() == formula::svMatrixCell ?
189 dynamic_cast<const ScMatrixCellResultToken*>(p) : nullptr);
190 if (pMatResult)
191 {
192 const ScMatrixFormulaCellToken* pNewMatFormula =
193 dynamic_cast<const ScMatrixFormulaCellToken*>(pMatResult);
194 if (pNewMatFormula && (pMatFormula->GetMatCols() <= 0 || pMatFormula->GetMatRows() <= 0))
195 {
196 SAL_WARN( "sc", "ScFormulaResult::SetToken: pNewMatFormula and pMatFormula, overriding matrix formula dimension; intended?");
197 pMatFormula->SetMatColsRows( pNewMatFormula->GetMatCols(),
198 pNewMatFormula->GetMatRows());
199 }
200 pMatFormula->Assign( *pMatResult);
201 p->DecRef();
202 }
203 else if (p)
204 {
205 // This may be the result of some constant expression like
206 // {="string"} that doesn't result in a matrix but still would
207 // display the result in all cells of this matrix formula.
208 pMatFormula->Assign( *p);
209 p->DecRef();
210 }
211 else
212 {
213 // NULL result? Well, if you say so ...
214 pMatFormula->ResetResult();
215 }
216 }
217 else
218 {
219 if (mbToken && mpToken)
220 mpToken->DecRef();
221 ResolveToken( p);
222 }
223}
224
226{
228 // Handle a result obtained from the interpreter to be assigned to a matrix
229 // formula cell's ScMatrixFormulaCellToken.
231 if (pMatFormula)
232 pMatFormula->SetUpperLeftDouble( f);
233 else
234 {
235 if (mbToken && mpToken)
236 mpToken->DecRef();
237 mfValue = f;
238 mbToken = false;
240 mbValueCached = true;
241 }
242}
243
245{
246 // Order is significant.
247 if (mnError != FormulaError::NONE)
248 return formula::svError;
249 if (mbEmpty)
251 if (!mbToken)
252 return formula::svDouble;
253 if (mpToken)
254 return mpToken->GetType();
255 return formula::svUnknown;
256}
257
259{
261 if (sv == formula::svMatrixCell)
262 // don't need to test for mpToken here, GetType() already did it
263 sv = static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftType();
264 return sv;
265}
266
268{
269 if (mbEmpty)
271 switch (GetType())
272 {
274 {
275 // don't need to test for mpToken here, GetType() already did it
276 const ScEmptyCellToken* p = dynamic_cast<const ScEmptyCellToken*>(
277 static_cast<const ScMatrixCellResultToken*>(
278 mpToken)->GetUpperLeftToken().get());
279 if (p)
280 return p->IsDisplayedAsString();
281 }
282 break;
284 {
285 const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken);
286 return p->IsEmptyDisplayedAsString();
287 }
288 break;
289 default:
290 break;
291 }
292 return false;
293}
294
295namespace {
296
297bool isValue( formula::StackVar sv )
298{
299 return sv == formula::svDouble || sv == formula::svError
300 || sv == formula::svEmptyCell
301 // The initial uninitialized result value is double 0.0, even if the type
302 // is unknown, so the interpreter asking for it gets that double
303 // instead of having to convert a string which may result in #VALUE!
304 // (otherwise the unknown would be neither error nor double nor string)
305 || sv == formula::svUnknown;
306}
307
308bool isString( formula::StackVar sv )
309{
310 switch (sv)
311 {
314 return true;
315 default:
316 break;
317 }
318
319 return false;
320}
321
322}
323
325{
327 return true;
328
329 return isValue(GetCellResultType());
330}
331
333{
334 switch (GetCellResultType())
335 {
338 return true;
339 default:
340 return false;
341 }
342}
343
345{
347 {
349 if (!aStr.isEmpty() && aStr.getString().indexOf('\n') != -1)
350 const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_TRUE;
351 else
352 const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_FALSE;
353 }
354 return meMultiline == MULTILINE_TRUE;
355}
356
357bool ScFormulaResult::GetErrorOrDouble( FormulaError& rErr, double& rVal ) const
358{
359 if (mbValueCached)
360 {
361 rVal = mfValue;
362 return true;
363 }
364
365 if (mnError != FormulaError::NONE)
366 {
367 rErr = mnError;
368 return true;
369 }
370
372 if (sv == formula::svError)
373 {
375 {
376 // don't need to test for mpToken here, GetType() already did it
377 rErr = static_cast<const ScMatrixCellResultToken*>(mpToken)->
378 GetUpperLeftToken()->GetError();
379 }
380 else if (mpToken)
381 {
382 rErr = mpToken->GetError();
383 }
384 }
385
386 if (rErr != FormulaError::NONE)
387 return true;
388
389 if (!isValue(sv))
390 return false;
391
392 rVal = GetDouble();
393 return true;
394}
395
397{
398 if (mbValueCached)
400
401 if (mnError != FormulaError::NONE)
403
405 FormulaError nErr = FormulaError::NONE;
406 if (sv == formula::svError)
407 {
409 {
410 // don't need to test for mpToken here, GetType() already did it
411 nErr = static_cast<const ScMatrixCellResultToken*>(mpToken)->
412 GetUpperLeftToken()->GetError();
413 }
414 else if (mpToken)
415 {
416 nErr = mpToken->GetError();
417 }
418 }
419
420 if (nErr != FormulaError::NONE)
421 return sc::FormulaResultValue(nErr);
422
423 if (isValue(sv))
425
426 if (!mbToken)
427 // String result type needs token.
428 return sc::FormulaResultValue();
429
430 if (isString(sv))
432
433 // Invalid
434 return sc::FormulaResultValue();
435}
436
438{
439 if (mnError != FormulaError::NONE)
440 return mnError;
442 if (sv == formula::svError)
443 {
445 // don't need to test for mpToken here, GetType() already did it
446 return static_cast<const ScMatrixCellResultToken*>(mpToken)->
447 GetUpperLeftToken()->GetError();
448 if (mpToken)
449 return mpToken->GetError();
450 }
451 return FormulaError::NONE;
452}
453
455{
456 mnError = nErr;
457 if (mnError != FormulaError::NONE)
458 mbValueCached = false;
459}
460
462{
463 if (mbToken)
464 return mpToken;
465 return nullptr;
466}
467
469{
471 // don't need to test for mpToken here, GetType() already did it
472 return static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftToken();
473 return GetToken();
474}
475
477{
478 if (mbValueCached)
479 return mfValue;
480
481 if (mbToken)
482 {
483 // Should really not be of type formula::svDouble here.
484 if (mpToken)
485 {
486 switch (mpToken->GetType())
487 {
489 return mpToken->GetDouble();
491 {
493 static_cast<const ScMatrixCellResultToken*>(mpToken);
494 if (p->GetUpperLeftType() == formula::svDouble)
495 return p->GetUpperLeftToken()->GetDouble();
496 }
497 break;
498 default:
499 ; // nothing
500 }
501 }
502 // Note that we reach here also for the default ctor and
503 // formula::svUnknown from GetType().
504 return 0.0;
505 }
506 if (mbEmpty)
507 return 0.0;
508 return mfValue;
509}
510
512{
513 if (mbToken && mpToken)
514 {
515 switch (mpToken->GetType())
516 {
519 return mpToken->GetString();
521 {
523 static_cast<const ScMatrixCellResultToken*>(mpToken);
524 if (p->GetUpperLeftType() == formula::svString)
525 return p->GetUpperLeftToken()->GetString();
526 }
527 break;
528 default:
529 ; // nothing
530 }
531 }
533}
534
536{
538 return mpToken->GetMatrix();
539 return nullptr;
540}
541
543{
545 {
546 const ScHybridCellToken* p = static_cast<const ScHybridCellToken*>(mpToken);
547 return p->GetFormula();
548 }
549 return OUString();
550}
551
553{
555 if (mbToken && mpToken)
556 {
558 SetDouble(f);
559 else
560 {
561 svl::SharedString aString = GetString();
562 OUString aFormula( GetHybridFormula());
563 mpToken->DecRef();
564 mpToken = new ScHybridCellToken( f, aString, aFormula, false);
565 mpToken->IncRef();
566 }
567 }
568 else
569 {
570 mfValue = f;
571 mbToken = false;
573 mbValueCached = true;
574 }
575}
576
578{
579 // Obtain values before changing anything.
580 double f = GetDouble();
581 OUString aFormula( GetHybridFormula());
583 if (mbToken && mpToken)
584 mpToken->DecRef();
585 mpToken = new ScHybridCellToken( f, rStr, aFormula, false);
586 mpToken->IncRef();
587 mbToken = true;
588}
589
591{
592 // Obtain values before changing anything.
593 double f = GetDouble();
594 OUString aFormula( GetHybridFormula());
597 if (mbToken && mpToken)
598 mpToken->DecRef();
599 // XXX NOTE: we can't use mbEmpty and mbEmptyDisplayedAsString here because
600 // GetType() intentionally returns svEmptyCell if mbEmpty==true. So stick
601 // it into the ScHybridCellToken.
602 mpToken = new ScHybridCellToken( f, aStr, aFormula, true);
603 mpToken->IncRef();
604 mbToken = true;
605}
606
607void ScFormulaResult::SetHybridFormula( const OUString & rFormula )
608{
609 // Obtain values before changing anything.
610 double f = GetDouble();
613 if (mbToken && mpToken)
614 mpToken->DecRef();
615 mpToken = new ScHybridCellToken( f, aStr, rFormula, false);
616 mpToken->IncRef();
617 mbToken = true;
618}
619
621{
623 if (mbToken && mpToken)
624 mpToken->DecRef();
625 mpToken = new ScMatrixFormulaCellToken(nCols, nRows, pMat, pUL);
626 mpToken->IncRef();
627 mbToken = true;
628}
629
631{
632 return (GetType() == formula::svMatrixCell ?
633 static_cast<const ScMatrixFormulaCellToken*>(mpToken) : nullptr);
634}
635
637{
639}
640
641/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Store a variable formula cell result, balancing between runtime performance and memory consumption.
static const Multiline MULTILINE_TRUE
void SetDouble(double f)
Set direct double.
const ScMatrixFormulaCellToken * GetMatrixFormulaCellToken() const
Get the const ScMatrixFormulaCellToken* if token is of that type, else NULL.
void ResetToDefaults()
Reset mnError, mbEmpty and mbEmptyDisplayedAsString to their defaults prior to assigning other types.
void SetHybridDouble(double f)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString(),...
void SetResultError(FormulaError nErr)
Set error code, don't touch token or double.
ScMatrixFormulaCellToken * GetMatrixFormulaCellTokenNonConst()
Get the ScMatrixFormulaCellToken* if token is of that type, else NULL.
Multiline meMultiline
ScFormulaResult & operator=(const ScFormulaResult &r)
Well, guess what ...
bool mbEmptyDisplayedAsString
formula::FormulaConstTokenRef GetToken() const
May be NULL if SetToken() did so, also if type formula::svDouble or formula::svError!
static const Multiline MULTILINE_FALSE
void SetToken(const formula::FormulaToken *p)
Sets a direct double if token type is formula::svDouble, or mbEmpty if formula::svEmptyCell,...
static const Multiline MULTILINE_UNKNOWN
const formula::FormulaToken * mpToken
ScFormulaResult()
Effectively type svUnknown.
ScConstMatrixRef GetMatrix() const
Return matrix if type formula::svMatrixCell and ScMatrix present, else NULL.
formula::FormulaConstTokenRef GetCellResultToken() const
Return upper left token if formula::svMatrixCell, else return GetToken().
double GetDouble() const
Return value if type formula::svDouble or formula::svHybridCell or formula::svMatrixCell and upper le...
bool IsEmptyDisplayedAsString() const
If type is formula::svEmptyCell (including matrix upper left) and should be displayed as empty string...
void SetHybridEmptyDisplayedAsString()
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridFormula(),...
bool GetErrorOrDouble(FormulaError &rErr, double &rVal) const
void SetHybridString(const svl::SharedString &rStr)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString()/SetHybr...
void ResolveToken(const formula::FormulaToken *p)
If token is of formula::svError set error code and decrement RefCount.
const svl::SharedString & GetString() const
Return string if type formula::svString or formula::svHybridCell or formula::svMatrixCell and upper l...
bool IsValueNoError() const
formula::StackVar GetType() const
Return type of result, including formula::svError, formula::svEmptyCell, formula::svDouble and formul...
void SetHybridFormula(const OUString &rFormula)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString()/SetHybr...
bool IsMultiline() const
Determines whether or not the result is a string containing more than one paragraph.
void SetMatrix(SCCOL nCols, SCROW nRows, const ScConstMatrixRef &pMat, const formula::FormulaToken *pUL)
bool IsValue() const
Test for cell result type formula::svDouble, including upper left if formula::svMatrixCell.
OUString GetHybridFormula() const
Return formula string if type formula::svHybridCell, else empty string.
void Assign(const ScFormulaResult &r)
Assignment as in operator=() but without return.
formula::StackVar GetCellResultType() const
If type is formula::svMatrixCell return the type of upper left element, else GetType()
FormulaError mnError
sc::FormulaResultValue GetResult() const
FormulaError GetResultError() const
Get error code if set or GetCellResultType() is formula::svError or svUnknown, else 0.
Transports the result from the interpreter to the formula cell.
Definition: token.hxx:295
Stores the matrix result at the formula cell, additionally the range the matrix formula occupies.
Definition: token.hxx:325
void Assign(const ScMatrixCellResultToken &r)
Assign matrix result, keep matrix formula dimension.
Definition: token.cxx:1051
SCCOL GetMatCols() const
Definition: token.hxx:347
SCROW GetMatRows() const
Definition: token.hxx:348
void SetUpperLeftDouble(double f)
Modify xUpperLeft if formula::svDouble, or create new formula::FormulaDoubleToken if not set yet.
Definition: token.cxx:1082
void ResetResult()
Reset matrix and upper left, keep matrix formula dimension.
Definition: token.cxx:1106
void SetMatColsRows(SCCOL nC, SCROW nR)
Definition: token.hxx:337
StackVar GetType() const
virtual const svl::SharedString & GetString() const
virtual const ScMatrix * GetMatrix() const
virtual double GetDouble() const
virtual FormulaError GetError() const
static const SharedString & getEmptyString()
OUString maString
FormulaError
void * p
#define SAL_WARN(area, stream)
aStr
NONE
Error
Value
::boost::intrusive_ptr< const FormulaToken > FormulaConstTokenRef
CAUTION! The following defines must be in the same namespace as the respective type.
::boost::intrusive_ptr< const ScMatrix > ScConstMatrixRef
Definition: types.hxx:26
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
RedlineType meType