xref: /trunk/main/sc/inc/formularesult.hxx (revision 38d50f7b)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #ifndef SC_FORMULARESULT_HXX
25 #define SC_FORMULARESULT_HXX
26 
27 #include "token.hxx"
28 
29 
30 /** Store a variable formula cell result, balancing between runtime performance
31     and memory consumption. */
32 class ScFormulaResult
33 {
34     typedef unsigned char Multiline;
35     static const Multiline MULTILINE_UNKNOWN = 0;
36     static const Multiline MULTILINE_FALSE   = 1;
37     static const Multiline MULTILINE_TRUE    = 2;
38 
39     union
40     {
41         double          mfValue;    // double result direct for performance and memory consumption
42         const formula::FormulaToken*  mpToken;    // if not, result token obtained from interpreter
43     };
44     sal_uInt16              mnError;    // error code
45     bool                mbToken :1; // whether content of union is a token
46     bool                mbEmpty :1; // empty cell result
47     bool                mbEmptyDisplayedAsString :1;    // only if mbEmpty
48     Multiline           meMultiline :2; // result is multiline
49 
50     /** Reset mnError, mbEmpty and mbEmptyDisplayedAsString to their defaults
51         prior to assigning other types */
52     inline  void                ResetToDefaults();
53 
54     /** If token is of formula::svError set error code and decrement RefCount.
55         If token is of formula::svEmptyCell set mbEmpty and mbEmptyAsString and
56         decrement RefCount.
57         If token is of formula::svDouble set mfValue and decrement RefCount.
58         Else assign token to mpToken. NULL is valid => svUnknown.
59         Other member variables are set accordingly.
60         @precondition: Token MUST had been IncRef'ed prior to this call!
61         @precondition: An already existing different mpToken MUST had been
62         DecRef'ed prior to this call, p will be assigned to mpToken if not
63         resolved.
64         ATTENTION! Token may get deleted in this call! */
65     inline  void                ResolveToken( const formula::FormulaToken * p );
66 
67 public:
68                                 /** Effectively type svUnknown. */
ScFormulaResult()69                                 ScFormulaResult()
70                                     : mpToken(NULL), mnError(0), mbToken(true),
71                                     mbEmpty(false), mbEmptyDisplayedAsString(false),
72                                     meMultiline(MULTILINE_UNKNOWN) {}
73 
ScFormulaResult(const ScFormulaResult & r)74                                 ScFormulaResult( const ScFormulaResult & r )
75                                     : mnError( r.mnError), mbToken( r.mbToken),
76                                     mbEmpty( r.mbEmpty),
77                                     mbEmptyDisplayedAsString( r.mbEmptyDisplayedAsString),
78                                     meMultiline( r.meMultiline)
79                                 {
80                                     if (mbToken)
81                                     {
82                                         mpToken = r.mpToken;
83                                         if (mpToken)
84                                         {
85                                             // Since matrix dimension and
86                                             // results are assigned to a matrix
87                                             // cell formula token we have to
88                                             // clone that instead of sharing it.
89                                             const ScMatrixFormulaCellToken* pMatFormula =
90                                                 r.GetMatrixFormulaCellToken();
91                                             if (pMatFormula)
92                                                 mpToken = new ScMatrixFormulaCellToken( *pMatFormula);
93                                             mpToken->IncRef();
94                                         }
95                                     }
96                                     else
97                                         mfValue = r.mfValue;
98                                 }
99 
100     /** Same comments as for SetToken() apply! */
ScFormulaResult(const formula::FormulaToken * p)101     explicit                    ScFormulaResult( const formula::FormulaToken* p )
102                                     : mnError(0), mbToken(false),
103                                     mbEmpty(false), mbEmptyDisplayedAsString(false),
104                                     meMultiline(MULTILINE_UNKNOWN)
105                                 {
106                                     SetToken( p);
107                                 }
108 
~ScFormulaResult()109                                 ~ScFormulaResult()
110                                 {
111                                     if (mbToken && mpToken)
112                                         mpToken->DecRef();
113                                 }
114 
115     /** Well, guess what ... */
116     inline  ScFormulaResult &   operator=( const ScFormulaResult & r );
117 
118     /** Assignment as in operator=() but without return */
119     inline  void                Assign( const ScFormulaResult & r );
120 
121     /** Sets a direct double if token type is formula::svDouble, or mbEmpty if
122         formula::svEmptyCell, else token. If p is NULL, that is set as well, effectively
123         resulting in GetType()==svUnknown. If the already existing result is
124         ScMatrixFormulaCellToken, the upper left ist set to token.
125 
126         ATTENTION! formula::FormulaToken had to be allocated using 'new' and if of type
127         formula::svDouble and no RefCount was set may not be used after this call
128         because it was deleted after decrement! */
129     inline  void                SetToken( const formula::FormulaToken* p );
130 
131     /** May be NULL if SetToken() did so, also if type formula::svDouble or formula::svError! */
132     inline  formula::FormulaConstTokenRef     GetToken() const;
133 
134     /** Return upper left token if formula::svMatrixCell, else return GetToken().
135         May be NULL if SetToken() did so, also if type formula::svDouble or formula::svError! */
136     inline  formula::FormulaConstTokenRef     GetCellResultToken() const;
137 
138     /** Return type of result, including formula::svError, formula::svEmptyCell, formula::svDouble and
139         formula::svMatrixCell. */
140     inline  formula::StackVar            GetType() const;
141 
142     /** If type is formula::svMatrixCell return the type of upper left element, else
143         GetType() */
144     inline  formula::StackVar            GetCellResultType() const;
145 
146     /** If type is formula::svEmptyCell (including matrix upper left) and should be
147         displayed as empty string */
148     inline  bool                IsEmptyDisplayedAsString() const;
149 
150     /** Test for cell result type formula::svDouble, including upper left if
151         formula::svMatrixCell. Also included is formula::svError for legacy, because previously
152         an error result was treated like a numeric value at some places in
153         ScFormulaCell. Also included is formula::svEmptyCell as a reference to an empty
154         cell usually is treated as numeric 0. Use GetCellResultType() for
155         details instead. */
156     inline  bool                IsValue() const;
157 
158     /** Determines whether or not the result is a string containing more than
159         one paragraph */
160     inline  bool                IsMultiline() const;
161 
162     /** Get error code if set or GetCellResultType() is formula::svError or svUnknown,
163         else 0. */
164     inline  sal_uInt16              GetResultError() const;
165 
166     /** Set error code, don't touch token or double. */
167     inline  void                SetResultError( sal_uInt16 nErr );
168 
169     /** Set direct double. Shouldn't be used externally except in
170         ScFormulaCell for rounded CalcAsShown or SetErrCode(). If
171         ScMatrixFormulaCellToken the token isn't replaced but upper left result
172         is modified instead, but only if it was of type formula::svDouble before or not
173         set at all. */
174     inline  void                SetDouble( double f );
175 
176     /** Return value if type formula::svDouble or formula::svHybridCell or formula::svMatrixCell and upper
177         left formula::svDouble, else 0.0 */
178     inline  double              GetDouble() const;
179 
180     /** Return string if type formula::svString or formula::svHybridCell or formula::svMatrixCell and
181         upper left formula::svString, else empty string. */
182     inline  const String &      GetString() const;
183 
184     /** Return matrix if type formula::svMatrixCell and ScMatrix present, else NULL. */
185     inline  ScConstMatrixRef    GetMatrix() const;
186 
187     /** Return formula string if type formula::svHybridCell, else empty string. */
188     inline  const String &      GetHybridFormula() const;
189 
190     /** Should only be used by import filters, best in the order
191         SetHybridDouble(), SetHybridString(), or only SetHybridString() for
192         formula string to be compiled later. */
193     inline  void                SetHybridDouble( double f );
194 
195     /** Should only be used by import filters, best in the order
196         SetHybridDouble(), SetHybridString()/SetHybridFormula(), or only
197         SetHybridFormula() for formula string to be compiled later. */
198     inline  void                SetHybridString( const String & rStr );
199 
200     /** Should only be used by import filters, best in the order
201         SetHybridDouble(), SetHybridString()/SetHybridFormula(), or only
202         SetHybridFormula() for formula string to be compiled later. */
203     inline  void                SetHybridFormula( const String & rFormula );
204 
205     /** Get the const ScMatrixFormulaCellToken* if token is of that type, else
206         NULL. */
207     inline const ScMatrixFormulaCellToken* GetMatrixFormulaCellToken() const;
208 
209     /** Get the ScMatrixFormulaCellToken* if token is of that type, else NULL.
210         Shouldn't be used externally except by ScFormulaCell::SetMatColsRows(). */
211     inline ScMatrixFormulaCellToken* GetMatrixFormulaCellTokenNonConst();
212 };
213 
214 
ResetToDefaults()215 inline void ScFormulaResult::ResetToDefaults()
216 {
217     mnError = 0;
218     mbEmpty = false;
219     mbEmptyDisplayedAsString = false;
220     meMultiline = MULTILINE_UNKNOWN;
221 }
222 
223 
ResolveToken(const formula::FormulaToken * p)224 inline void ScFormulaResult::ResolveToken( const formula::FormulaToken * p )
225 {
226     ResetToDefaults();
227     if (!p)
228     {
229         mpToken = p;
230         mbToken = true;
231     }
232     else
233     {
234         switch (p->GetType())
235         {
236             case formula::svError:
237                 mnError = p->GetError();
238                 p->DecRef();
239                 mbToken = false;
240                 // set in case mnError is 0 now, which shouldn't happen but ...
241                 mfValue = 0.0;
242                 meMultiline = MULTILINE_FALSE;
243                 break;
244             case formula::svEmptyCell:
245                 mbEmpty = true;
246                 mbEmptyDisplayedAsString = static_cast<const ScEmptyCellToken*>(p)->IsDisplayedAsString();
247                 p->DecRef();
248                 mbToken = false;
249                 meMultiline = MULTILINE_FALSE;
250                 break;
251             case formula::svDouble:
252                 mfValue = p->GetDouble();
253                 p->DecRef();
254                 mbToken = false;
255                 meMultiline = MULTILINE_FALSE;
256                 break;
257             default:
258                 mpToken = p;
259                 mbToken = true;
260         }
261     }
262 }
263 
264 
operator =(const ScFormulaResult & r)265 inline ScFormulaResult & ScFormulaResult::operator=( const ScFormulaResult & r )
266 {
267     Assign( r);
268     return *this;
269 }
270 
271 
Assign(const ScFormulaResult & r)272 inline void ScFormulaResult::Assign( const ScFormulaResult & r )
273 {
274     if (this == &r)
275         return;
276     if (r.mbEmpty)
277     {
278         if (mbToken && mpToken)
279             mpToken->DecRef();
280         mbToken = false;
281         mbEmpty = true;
282         mbEmptyDisplayedAsString = r.mbEmptyDisplayedAsString;
283         meMultiline = r.meMultiline;
284     }
285     else if (r.mbToken)
286     {
287         // Matrix formula cell token must be cloned, see copy-ctor.
288         const ScMatrixFormulaCellToken* pMatFormula =
289             r.GetMatrixFormulaCellToken();
290         if (pMatFormula)
291             SetToken( new ScMatrixFormulaCellToken( *pMatFormula));
292         else
293             SetToken( r.mpToken);
294     }
295     else
296         SetDouble( r.mfValue);
297     // If there was an error there will be an error, no matter what Set...()
298     // methods did.
299     mnError = r.mnError;
300 }
301 
302 
SetToken(const formula::FormulaToken * p)303 inline void ScFormulaResult::SetToken( const formula::FormulaToken* p )
304 {
305     ResetToDefaults();
306     if (p)
307         p->IncRef();
308     // Handle a result obtained from the interpreter to be assigned to a matrix
309     // formula cell's ScMatrixFormulaCellToken.
310     ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst();
311     if (pMatFormula)
312     {
313         const ScMatrixCellResultToken* pMatResult =
314             (p && p->GetType() == formula::svMatrixCell ?
315              dynamic_cast<const ScMatrixCellResultToken*>(p) : NULL);
316         if (pMatResult)
317         {
318             const ScMatrixFormulaCellToken* pNewMatFormula =
319                 dynamic_cast<const ScMatrixFormulaCellToken*>(pMatResult);
320             if (pNewMatFormula)
321             {
322                 DBG_ERRORFILE( "ScFormulaResult::SetToken: pNewMatFormula and pMatFormula, overriding matrix formula dimension; intended?");
323                 pMatFormula->SetMatColsRows( pNewMatFormula->GetMatCols(),
324                         pNewMatFormula->GetMatRows());
325             }
326             pMatFormula->Assign( *pMatResult);
327             p->DecRef();
328         }
329         else if (p)
330         {
331             // This may be the result of some constant expression like
332             // {="string"} that doesn't result in a matrix but still would
333             // display the result in all cells of this matrix formula.
334             pMatFormula->Assign( *p);
335             p->DecRef();
336         }
337         else
338         {
339             // NULL result? Well, if you say so ...
340             pMatFormula->ResetResult();
341         }
342     }
343     else
344     {
345         if (mbToken && mpToken)
346             mpToken->DecRef();
347         ResolveToken( p);
348     }
349 }
350 
351 
SetDouble(double f)352 inline void ScFormulaResult::SetDouble( double f )
353 {
354     ResetToDefaults();
355     // Handle a result obtained from the interpreter to be assigned to a matrix
356     // formula cell's ScMatrixFormulaCellToken.
357     ScMatrixFormulaCellToken* pMatFormula = GetMatrixFormulaCellTokenNonConst();
358     if (pMatFormula)
359         pMatFormula->SetUpperLeftDouble( f);
360     else
361     {
362         if (mbToken && mpToken)
363             mpToken->DecRef();
364         mfValue = f;
365         mbToken = false;
366         meMultiline = MULTILINE_FALSE;
367     }
368 }
369 
370 
GetType() const371 inline formula::StackVar ScFormulaResult::GetType() const
372 {
373     // Order is significant.
374     if (mnError)
375         return formula::svError;
376     if (mbEmpty)
377         return formula::svEmptyCell;
378     if (!mbToken)
379         return formula::svDouble;
380     if (mpToken)
381         return mpToken->GetType();
382     return formula::svUnknown;
383 }
384 
385 
GetCellResultType() const386 inline formula::StackVar ScFormulaResult::GetCellResultType() const
387 {
388     formula::StackVar sv = GetType();
389     if (sv == formula::svMatrixCell)
390         // don't need to test for mpToken here, GetType() already did it
391         sv = static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftType();
392     return sv;
393 }
394 
395 
IsEmptyDisplayedAsString() const396 inline bool ScFormulaResult::IsEmptyDisplayedAsString() const
397 {
398     if (mbEmpty)
399         return mbEmptyDisplayedAsString;
400     if (GetType() == formula::svMatrixCell)
401     {
402         // don't need to test for mpToken here, GetType() already did it
403         const ScEmptyCellToken* p = dynamic_cast<const ScEmptyCellToken*>(
404                 static_cast<const ScMatrixCellResultToken*>(
405                     mpToken)->GetUpperLeftToken().operator->());
406         if (p)
407             return p->IsDisplayedAsString();
408     }
409     return false;
410 }
411 
412 
IsValue() const413 inline bool ScFormulaResult::IsValue() const
414 {
415     formula::StackVar sv = GetCellResultType();
416     return sv == formula::svDouble || sv == formula::svError || sv == formula::svEmptyCell;
417 }
418 
IsMultiline() const419 inline bool ScFormulaResult::IsMultiline() const
420 {
421     if (meMultiline == MULTILINE_UNKNOWN)
422     {
423         const String& rStr = GetString();
424         if (rStr.Len() && rStr.Search( _LF ) != STRING_NOTFOUND)
425             const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_TRUE;
426         else
427             const_cast<ScFormulaResult*>(this)->meMultiline = MULTILINE_FALSE;
428     }
429     return meMultiline == MULTILINE_TRUE;
430 }
431 
432 
GetResultError() const433 inline sal_uInt16 ScFormulaResult::GetResultError() const
434 {
435     if (mnError)
436         return mnError;
437     formula::StackVar sv = GetCellResultType();
438     if (sv == formula::svError)
439     {
440         if (GetType() == formula::svMatrixCell)
441             // don't need to test for mpToken here, GetType() already did it
442             return static_cast<const ScMatrixCellResultToken*>(mpToken)->
443                 GetUpperLeftToken()->GetError();
444         if (mpToken)
445             return mpToken->GetError();
446     }
447     return 0;
448 }
449 
450 
SetResultError(sal_uInt16 nErr)451 inline void ScFormulaResult::SetResultError( sal_uInt16 nErr )
452 {
453     mnError = nErr;
454 }
455 
456 
GetToken() const457 inline formula::FormulaConstTokenRef ScFormulaResult::GetToken() const
458 {
459     if (mbToken)
460         return mpToken;
461     return NULL;
462 }
463 
464 
GetCellResultToken() const465 inline formula::FormulaConstTokenRef ScFormulaResult::GetCellResultToken() const
466 {
467     if (GetType() == formula::svMatrixCell)
468         // don't need to test for mpToken here, GetType() already did it
469         return static_cast<const ScMatrixCellResultToken*>(mpToken)->GetUpperLeftToken();
470     return GetToken();
471 }
472 
473 
GetDouble() const474 inline double ScFormulaResult::GetDouble() const
475 {
476     if (mbToken)
477     {
478         // Should really not be of type formula::svDouble here.
479         if (mpToken)
480         {
481             switch (mpToken->GetType())
482             {
483                 case formula::svHybridCell:
484                     return mpToken->GetDouble();
485                 case formula::svMatrixCell:
486                     {
487                         const ScMatrixCellResultToken* p =
488                             static_cast<const ScMatrixCellResultToken*>(mpToken);
489                         if (p->GetUpperLeftType() == formula::svDouble)
490                             return p->GetUpperLeftToken()->GetDouble();
491                     }
492                     break;
493                 default:
494                     ;   // nothing
495             }
496         }
497         return 0.0;
498     }
499     if (mbEmpty)
500         return 0.0;
501     return mfValue;
502 }
503 
504 
GetString() const505 inline const String & ScFormulaResult::GetString() const
506 {
507     if (mbToken && mpToken)
508     {
509         switch (mpToken->GetType())
510         {
511             case formula::svString:
512             case formula::svHybridCell:
513                 return mpToken->GetString();
514             case formula::svMatrixCell:
515                 {
516                     const ScMatrixCellResultToken* p =
517                         static_cast<const ScMatrixCellResultToken*>(mpToken);
518                     if (p->GetUpperLeftType() == formula::svString)
519                         return p->GetUpperLeftToken()->GetString();
520                 }
521                 break;
522             default:
523                 ;   // nothing
524         }
525     }
526     return EMPTY_STRING;
527 }
528 
529 
GetMatrix() const530 inline ScConstMatrixRef ScFormulaResult::GetMatrix() const
531 {
532     if (GetType() == formula::svMatrixCell)
533         return static_cast<const ScToken*>(mpToken)->GetMatrix();
534     return NULL;
535 }
536 
537 
GetHybridFormula() const538 inline const String & ScFormulaResult::GetHybridFormula() const
539 {
540     if (GetType() == formula::svHybridCell)
541     {
542         const ScHybridCellToken* p = dynamic_cast<const ScHybridCellToken*>(mpToken);
543         if (p)
544             return p->GetFormula();
545     }
546     return EMPTY_STRING;
547 }
548 
549 
SetHybridDouble(double f)550 inline void ScFormulaResult::SetHybridDouble( double f )
551 {
552     ResetToDefaults();
553     if (mbToken && mpToken)
554     {
555         String aString( GetString());
556         String aFormula( GetHybridFormula());
557         mpToken->DecRef();
558         mpToken = new ScHybridCellToken( f, aString, aFormula);
559         mpToken->IncRef();
560     }
561     else
562     {
563         mfValue = f;
564         mbToken = false;
565         meMultiline = MULTILINE_FALSE;
566     }
567 }
568 
569 
SetHybridString(const String & rStr)570 inline void ScFormulaResult::SetHybridString( const String & rStr )
571 {
572     // Obtain values before changing anything.
573     double f = GetDouble();
574     String aFormula( GetHybridFormula());
575     ResetToDefaults();
576     if (mbToken && mpToken)
577         mpToken->DecRef();
578     mpToken = new ScHybridCellToken( f, rStr, aFormula);
579     mpToken->IncRef();
580     mbToken = true;
581 }
582 
583 
SetHybridFormula(const String & rFormula)584 inline void ScFormulaResult::SetHybridFormula( const String & rFormula )
585 {
586     // Obtain values before changing anything.
587     double f = GetDouble();
588     String aStr( GetString());
589     ResetToDefaults();
590     if (mbToken && mpToken)
591         mpToken->DecRef();
592     mpToken = new ScHybridCellToken( f, aStr, rFormula);
593     mpToken->IncRef();
594     mbToken = true;
595 }
596 
597 
GetMatrixFormulaCellToken() const598 inline const ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellToken() const
599 {
600     return (GetType() == formula::svMatrixCell ?
601             dynamic_cast<const ScMatrixFormulaCellToken*>(mpToken) : NULL);
602 }
603 
604 
GetMatrixFormulaCellTokenNonConst()605 inline ScMatrixFormulaCellToken* ScFormulaResult::GetMatrixFormulaCellTokenNonConst()
606 {
607     return const_cast<ScMatrixFormulaCellToken*>( GetMatrixFormulaCellToken());
608 }
609 
610 
611 #endif // SC_FORMULARESULT_HXX
612