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 
21 #include <algorithm>
22 
23 #include <string.h>
24 #include <limits.h>
25 #include <osl/diagnose.h>
26 #include <sal/log.hxx>
27 
28 #include <com/sun/star/sheet/FormulaToken.hpp>
29 #include <formula/errorcodes.hxx>
30 #include <formula/token.hxx>
31 #include <formula/tokenarray.hxx>
32 #include <formula/FormulaCompiler.hxx>
33 #include <formula/compiler.hxx>
34 #include <svl/sharedstringpool.hxx>
35 #include <memory>
36 
37 namespace formula
38 {
39     using namespace com::sun::star;
40 
41 
42 // --- helpers --------------------------------------------------------------
43 
lcl_IsReference(OpCode eOp,StackVar eType)44 static bool lcl_IsReference( OpCode eOp, StackVar eType )
45 {
46     return
47         (eOp == ocPush && (eType == svSingleRef || eType == svDoubleRef))
48         || (eOp == ocColRowNameAuto && eType == svDoubleRef)
49         || (eOp == ocColRowName && eType == svSingleRef)
50         || (eOp == ocMatRef && eType == svSingleRef)
51         ;
52 }
53 
54 // --- class FormulaToken --------------------------------------------------------
55 
FormulaToken(StackVar eTypeP,OpCode e)56 FormulaToken::FormulaToken( StackVar eTypeP, OpCode e ) :
57     eOp(e), eType( eTypeP ), mnRefCnt(0)
58 {
59 }
60 
FormulaToken(const FormulaToken & r)61 FormulaToken::FormulaToken( const FormulaToken& r ) :
62     eOp(r.eOp), eType( r.eType ), mnRefCnt(0)
63 {
64 }
65 
~FormulaToken()66 FormulaToken::~FormulaToken()
67 {
68 }
69 
IsFunction() const70 bool FormulaToken::IsFunction() const
71 {
72     return (eOp != ocPush && eOp != ocBad && eOp != ocColRowName &&
73             eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea &&
74             eOp != ocTableRef &&
75            (GetByte() != 0                                                  // x parameters
76         || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)   // no parameter
77         || FormulaCompiler::IsOpCodeJumpCommand( eOp )                      // @ jump commands
78         || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)     // one parameter
79         || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)     // x parameters (cByte==0 in
80                                                                             // FuncAutoPilot)
81         || eOp == ocMacro || eOp == ocExternal                  // macros, AddIns
82         || eOp == ocAnd || eOp == ocOr                          // former binary, now x parameters
83         || (eOp >= ocInternalBegin && eOp <= ocInternalEnd)     // internal
84         ));
85 }
86 
87 
GetParamCount() const88 sal_uInt8 FormulaToken::GetParamCount() const
89 {
90     if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
91          !FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
92          eOp != ocPercentSign )
93         return 0;       // parameters and specials
94                         // ocIf... jump commands not for FAP, have cByte then
95 //2do: bool parameter whether FAP or not?
96     else if ( GetByte() )
97         return GetByte();   // all functions, also ocExternal and ocMacro
98     else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP)
99         return 2;           // binary
100     else if ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
101             || eOp == ocPercentSign)
102         return 1;           // unary
103     else if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
104         return 0;           // no parameter
105     else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)
106         return 1;           // one parameter
107     else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
108         return 1;           // only the condition counts as parameter
109     else
110         return 0;           // all the rest, no Parameter, or
111                             // if so then it should be in cByte
112 }
113 
IsExternalRef() const114 bool FormulaToken::IsExternalRef() const
115 {
116     bool bRet = false;
117     switch (eType)
118     {
119         case svExternalSingleRef:
120         case svExternalDoubleRef:
121         case svExternalName:
122             bRet = true;
123             break;
124         default:
125             bRet = false;
126             break;
127     }
128     return bRet;
129 }
130 
IsRef() const131 bool FormulaToken::IsRef() const
132 {
133     switch (eType)
134     {
135         case svSingleRef:
136         case svDoubleRef:
137         case svExternalSingleRef:
138         case svExternalDoubleRef:
139             return true;
140         default:
141             if (eOp == ocTableRef)
142                 return true;
143     }
144 
145     return false;
146 }
147 
IsInForceArray() const148 bool FormulaToken::IsInForceArray() const
149 {
150     ParamClass eParam = GetInForceArray();
151     return eParam == ParamClass::ForceArray || eParam == ParamClass::ReferenceOrForceArray
152         || eParam == ParamClass::ReferenceOrRefArray || eParam == ParamClass::ForceArrayReturn;
153 }
154 
operator ==(const FormulaToken & rToken) const155 bool FormulaToken::operator==( const FormulaToken& rToken ) const
156 {
157     // don't compare reference count!
158     return  eType == rToken.eType && GetOpCode() == rToken.GetOpCode();
159 }
160 
161 
162 // --- virtual dummy methods -------------------------------------------------
163 
GetByte() const164 sal_uInt8 FormulaToken::GetByte() const
165 {
166     // ok to be called for any derived class
167     return 0;
168 }
169 
SetByte(sal_uInt8)170 void FormulaToken::SetByte( sal_uInt8 )
171 {
172     assert( !"virtual dummy called" );
173 }
174 
GetInForceArray() const175 ParamClass FormulaToken::GetInForceArray() const
176 {
177     // ok to be called for any derived class
178     return ParamClass::Unknown;
179 }
180 
SetInForceArray(ParamClass)181 void FormulaToken::SetInForceArray( ParamClass )
182 {
183     assert( !"virtual dummy called" );
184 }
185 
GetDouble() const186 double FormulaToken::GetDouble() const
187 {
188     // This Get is worth an assert.
189     assert( !"virtual dummy called" );
190     return 0.0;
191 }
192 
GetDoubleAsReference()193 double & FormulaToken::GetDoubleAsReference()
194 {
195     // This Get is worth an assert.
196     assert( !"virtual dummy called" );
197     static double fVal = 0.0;
198     return fVal;
199 }
200 
GetDoubleType() const201 sal_Int16 FormulaToken::GetDoubleType() const
202 {
203     SAL_WARN( "formula.core", "FormulaToken::GetDoubleType: virtual dummy called" );
204     return 0;
205 }
206 
SetDoubleType(sal_Int16)207 void FormulaToken::SetDoubleType( sal_Int16 )
208 {
209     assert( !"virtual dummy called" );
210 }
211 
212 const svl::SharedString INVALID_STRING;
213 
GetString() const214 const svl::SharedString & FormulaToken::GetString() const
215 {
216     SAL_WARN( "formula.core", "FormulaToken::GetString: virtual dummy called" );
217     return INVALID_STRING; // invalid string
218 }
219 
SetString(const svl::SharedString &)220 void FormulaToken::SetString( const svl::SharedString& )
221 {
222     assert( !"virtual dummy called" );
223 }
224 
GetIndex() const225 sal_uInt16 FormulaToken::GetIndex() const
226 {
227     SAL_WARN( "formula.core", "FormulaToken::GetIndex: virtual dummy called" );
228     return 0;
229 }
230 
SetIndex(sal_uInt16)231 void FormulaToken::SetIndex( sal_uInt16 )
232 {
233     assert( !"virtual dummy called" );
234 }
235 
GetSheet() const236 sal_Int16 FormulaToken::GetSheet() const
237 {
238     SAL_WARN( "formula.core", "FormulaToken::GetSheet: virtual dummy called" );
239     return -1;
240 }
241 
SetSheet(sal_Int16)242 void FormulaToken::SetSheet( sal_Int16 )
243 {
244     assert( !"virtual dummy called" );
245 }
246 
GetJump() const247 short* FormulaToken::GetJump() const
248 {
249     SAL_WARN( "formula.core", "FormulaToken::GetJump: virtual dummy called" );
250     return nullptr;
251 }
252 
253 
GetExternal() const254 const OUString& FormulaToken::GetExternal() const
255 {
256     SAL_WARN( "formula.core", "FormulaToken::GetExternal: virtual dummy called" );
257     static  OUString              aDummyString;
258     return aDummyString;
259 }
260 
GetFAPOrigToken() const261 FormulaToken* FormulaToken::GetFAPOrigToken() const
262 {
263     SAL_WARN( "formula.core", "FormulaToken::GetFAPOrigToken: virtual dummy called" );
264     return nullptr;
265 }
266 
GetError() const267 FormulaError FormulaToken::GetError() const
268 {
269     SAL_WARN( "formula.core", "FormulaToken::GetError: virtual dummy called" );
270     return FormulaError::NONE;
271 }
272 
SetError(FormulaError)273 void FormulaToken::SetError( FormulaError )
274 {
275     assert( !"virtual dummy called" );
276 }
277 
GetSingleRef() const278 const ScSingleRefData* FormulaToken::GetSingleRef() const
279 {
280     OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
281     return nullptr;
282 }
283 
GetSingleRef()284 ScSingleRefData* FormulaToken::GetSingleRef()
285 {
286     OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
287     return nullptr;
288 }
289 
GetDoubleRef() const290 const ScComplexRefData* FormulaToken::GetDoubleRef() const
291 {
292     OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
293     return nullptr;
294 }
295 
GetDoubleRef()296 ScComplexRefData* FormulaToken::GetDoubleRef()
297 {
298     OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
299     return nullptr;
300 }
301 
GetSingleRef2() const302 const ScSingleRefData* FormulaToken::GetSingleRef2() const
303 {
304     OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
305     return nullptr;
306 }
307 
GetSingleRef2()308 ScSingleRefData* FormulaToken::GetSingleRef2()
309 {
310     OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
311     return nullptr;
312 }
313 
GetMatrix() const314 const ScMatrix* FormulaToken::GetMatrix() const
315 {
316     OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
317     return nullptr;
318 }
319 
GetMatrix()320 ScMatrix* FormulaToken::GetMatrix()
321 {
322     OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
323     return nullptr;
324 }
325 
GetJumpMatrix() const326 ScJumpMatrix* FormulaToken::GetJumpMatrix() const
327 {
328     OSL_FAIL( "FormulaToken::GetJumpMatrix: virtual dummy called" );
329     return nullptr;
330 }
GetRefList() const331 const std::vector<ScComplexRefData>* FormulaToken::GetRefList() const
332 {
333     OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
334     return nullptr;
335 }
336 
GetRefList()337 std::vector<ScComplexRefData>* FormulaToken::GetRefList()
338 {
339     OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
340     return nullptr;
341 }
342 
TextEqual(const FormulaToken & rToken) const343 bool FormulaToken::TextEqual( const FormulaToken& rToken ) const
344 {
345     return *this == rToken;
346 }
347 
348 // real implementations of virtual functions
349 
350 
GetByte() const351 sal_uInt8   FormulaByteToken::GetByte() const           { return nByte; }
SetByte(sal_uInt8 n)352 void        FormulaByteToken::SetByte( sal_uInt8 n )    { nByte = n; }
GetInForceArray() const353 ParamClass  FormulaByteToken::GetInForceArray() const    { return eInForceArray; }
SetInForceArray(ParamClass c)354 void        FormulaByteToken::SetInForceArray( ParamClass c ) { eInForceArray = c; }
operator ==(const FormulaToken & r) const355 bool FormulaByteToken::operator==( const FormulaToken& r ) const
356 {
357     return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
358         eInForceArray == r.GetInForceArray();
359 }
360 
361 
GetFAPOrigToken() const362 FormulaToken* FormulaFAPToken::GetFAPOrigToken() const  { return pOrigToken.get(); }
operator ==(const FormulaToken & r) const363 bool FormulaFAPToken::operator==( const FormulaToken& r ) const
364 {
365     return FormulaByteToken::operator==( r ) && pOrigToken == r.GetFAPOrigToken();
366 }
367 
368 
GetJump() const369 short*      FormulaJumpToken::GetJump() const                   { return pJump.get(); }
GetInForceArray() const370 ParamClass  FormulaJumpToken::GetInForceArray() const           { return eInForceArray; }
SetInForceArray(ParamClass c)371 void        FormulaJumpToken::SetInForceArray( ParamClass c )   { eInForceArray = c; }
operator ==(const FormulaToken & r) const372 bool FormulaJumpToken::operator==( const FormulaToken& r ) const
373 {
374     return FormulaToken::operator==( r ) && pJump[0] == r.GetJump()[0] &&
375         memcmp( pJump.get()+1, r.GetJump()+1, pJump[0] * sizeof(short) ) == 0 &&
376         eInForceArray == r.GetInForceArray();
377 }
~FormulaJumpToken()378 FormulaJumpToken::~FormulaJumpToken()
379 {
380 }
381 
382 
AddFormulaToken(const sheet::FormulaToken & rToken,svl::SharedStringPool & rSPool,ExternalReferenceHelper *)383 bool FormulaTokenArray::AddFormulaToken(
384     const sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, ExternalReferenceHelper* /*pExtRef*/)
385 {
386     bool bError = false;
387     const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode);      //! assuming equal values for the moment
388 
389     const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
390     switch ( eClass )
391     {
392         case uno::TypeClass_VOID:
393             // empty data -> use AddOpCode (does some special cases)
394             AddOpCode( eOpCode );
395             break;
396         case uno::TypeClass_DOUBLE:
397             // double is only used for "push"
398             if ( eOpCode == ocPush )
399                 AddDouble( rToken.Data.get<double>() );
400             else
401                 bError = true;
402             break;
403         case uno::TypeClass_LONG:
404             {
405                 // long is svIndex, used for name / database area, or "byte" for spaces
406                 sal_Int32 nValue = rToken.Data.get<sal_Int32>();
407                 if ( eOpCode == ocDBArea )
408                     Add( new formula::FormulaIndexToken( eOpCode, static_cast<sal_uInt16>(nValue) ) );
409                 else if ( eOpCode == ocTableRef )
410                     bError = true;  /* TODO: implementation */
411                 else if ( eOpCode == ocSpaces )
412                     Add( new formula::FormulaByteToken( ocSpaces, static_cast<sal_uInt8>(nValue) ) );
413                 else
414                     bError = true;
415             }
416             break;
417         case uno::TypeClass_STRING:
418             {
419                 OUString aStrVal( rToken.Data.get<OUString>() );
420                 if ( eOpCode == ocPush )
421                     AddString(rSPool.intern(aStrVal));
422                 else if ( eOpCode == ocBad )
423                     AddBad( aStrVal );
424                 else if ( eOpCode == ocStringXML )
425                     AddStringXML( aStrVal );
426                 else if ( eOpCode == ocExternal || eOpCode == ocMacro )
427                     Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) );
428                 else
429                     bError = true;      // unexpected string: don't know what to do with it
430             }
431             break;
432         default:
433             bError = true;
434     } // switch ( eClass )
435     return bError;
436 }
437 
Fill(const uno::Sequence<sheet::FormulaToken> & rSequence,svl::SharedStringPool & rSPool,ExternalReferenceHelper * pExtRef)438 bool FormulaTokenArray::Fill(
439     const uno::Sequence<sheet::FormulaToken>& rSequence,
440     svl::SharedStringPool& rSPool, ExternalReferenceHelper* pExtRef )
441 {
442     bool bError = false;
443     const sal_Int32 nCount = rSequence.getLength();
444     for (sal_Int32 nPos=0; nPos<nCount; nPos++)
445     {
446         bool bOneError = AddFormulaToken(rSequence[nPos], rSPool, pExtRef);
447         if (bOneError)
448         {
449             AddOpCode( ocErrName);  // add something that indicates an error
450             bError = true;
451         }
452     }
453     return bError;
454 }
455 
DelRPN()456 void FormulaTokenArray::DelRPN()
457 {
458     if( nRPN )
459     {
460         FormulaToken** p = pRPN;
461         for( sal_uInt16 i = 0; i < nRPN; i++ )
462         {
463             (*p++)->DecRef();
464         }
465         delete [] pRPN;
466     }
467     pRPN = nullptr;
468     nRPN = 0;
469 }
470 
FirstToken() const471 FormulaToken* FormulaTokenArray::FirstToken() const
472 {
473     if (!pCode || nLen == 0)
474         return nullptr;
475     return pCode[0];
476 }
477 
PeekPrev(sal_uInt16 & nIdx) const478 FormulaToken* FormulaTokenArray::PeekPrev( sal_uInt16 & nIdx ) const
479 {
480     if (0 < nIdx && nIdx <= nLen)
481         return pCode[--nIdx];
482     return nullptr;
483 }
484 
FirstRPNToken() const485 FormulaToken* FormulaTokenArray::FirstRPNToken() const
486 {
487     if (!pRPN || nRPN == 0)
488         return nullptr;
489     return pRPN[0];
490 }
491 
HasReferences() const492 bool FormulaTokenArray::HasReferences() const
493 {
494     for (auto i: Tokens())
495     {
496         if (i->IsRef())
497             return true;
498     }
499 
500     for (auto i: RPNTokens())
501     {
502         if (i->IsRef())
503             return true;
504     }
505 
506     return false;
507 }
508 
HasExternalRef() const509 bool FormulaTokenArray::HasExternalRef() const
510 {
511     for (auto i: Tokens())
512     {
513         if (i->IsExternalRef())
514             return true;
515     }
516     return false;
517 }
518 
HasOpCode(OpCode eOp) const519 bool FormulaTokenArray::HasOpCode( OpCode eOp ) const
520 {
521     for (auto i: Tokens())
522     {
523         if (i->GetOpCode() == eOp)
524             return true;
525     }
526     return false;
527 }
528 
HasOpCodeRPN(OpCode eOp) const529 bool FormulaTokenArray::HasOpCodeRPN( OpCode eOp ) const
530 {
531     for (auto i: RPNTokens())
532     {
533         if (i->GetOpCode() == eOp)
534             return true;
535     }
536     return false;
537 }
538 
HasNameOrColRowName() const539 bool FormulaTokenArray::HasNameOrColRowName() const
540 {
541     for (auto i: Tokens())
542     {
543         if (i->GetType() == svIndex || i->GetOpCode() == ocColRowName )
544             return true;
545     }
546     return false;
547 }
548 
HasOpCodes(const unordered_opcode_set & rOpCodes) const549 bool FormulaTokenArray::HasOpCodes(const unordered_opcode_set& rOpCodes) const
550 {
551     for (auto i: Tokens())
552     {
553         if (rOpCodes.count(i->GetOpCode()) > 0)
554             return true;
555     }
556 
557     return false;
558 }
559 
FormulaTokenArray()560 FormulaTokenArray::FormulaTokenArray() :
561     pRPN(nullptr),
562     nLen(0),
563     nRPN(0),
564     nError(FormulaError::NONE),
565     nMode(ScRecalcMode::NORMAL),
566     bHyperLink(false),
567     mbFromRangeName(false),
568     mbShareable(true),
569     mbFinalized(false)
570 {
571 }
572 
FormulaTokenArray(const FormulaTokenArray & rArr)573 FormulaTokenArray::FormulaTokenArray( const FormulaTokenArray& rArr )
574 {
575     Assign( rArr );
576 }
577 
~FormulaTokenArray()578 FormulaTokenArray::~FormulaTokenArray()
579 {
580     FormulaTokenArray::Clear();
581 }
582 
Finalize()583 void FormulaTokenArray::Finalize()
584 {
585     if( nLen && !mbFinalized )
586     {
587         // Add() overallocates, so reallocate to the minimum needed size.
588         std::unique_ptr<FormulaToken*[]> newCode(new FormulaToken*[ nLen ]);
589         std::copy(&pCode[0], &pCode[nLen], newCode.get());
590         pCode = std::move( newCode );
591         mbFinalized = true;
592     }
593 }
594 
Assign(const FormulaTokenArray & r)595 void FormulaTokenArray::Assign( const FormulaTokenArray& r )
596 {
597     nLen   = r.nLen;
598     nRPN   = r.nRPN;
599     nError = r.nError;
600     nMode  = r.nMode;
601     bHyperLink = r.bHyperLink;
602     mbFromRangeName = r.mbFromRangeName;
603     mbShareable = r.mbShareable;
604     mbFinalized = r.mbFinalized;
605     pCode  = nullptr;
606     pRPN   = nullptr;
607     FormulaToken** pp;
608     if( nLen )
609     {
610         pCode.reset(new FormulaToken*[ nLen ]);
611         pp = pCode.get();
612         memcpy( pp, r.pCode.get(), nLen * sizeof( FormulaToken* ) );
613         for( sal_uInt16 i = 0; i < nLen; i++ )
614             (*pp++)->IncRef();
615         mbFinalized = true;
616     }
617     if( nRPN )
618     {
619         pp = pRPN = new FormulaToken*[ nRPN ];
620         memcpy( pp, r.pRPN, nRPN * sizeof( FormulaToken* ) );
621         for( sal_uInt16 i = 0; i < nRPN; i++ )
622             (*pp++)->IncRef();
623     }
624 }
625 
626 /// Optimisation for efficiently creating StringXML placeholders
Assign(sal_uInt16 nCode,FormulaToken ** pTokens)627 void FormulaTokenArray::Assign( sal_uInt16 nCode, FormulaToken **pTokens )
628 {
629     assert( nLen == 0 );
630     assert( pCode == nullptr );
631 
632     nLen = nCode;
633     pCode.reset(new FormulaToken*[ nLen ]);
634     mbFinalized = true;
635 
636     for( sal_uInt16 i = 0; i < nLen; i++ )
637     {
638         FormulaToken *t = pTokens[ i ];
639         assert( t->GetOpCode() == ocStringXML );
640         pCode[ i ] = t;
641         t->IncRef();
642     }
643 }
644 
operator =(const FormulaTokenArray & rArr)645 FormulaTokenArray& FormulaTokenArray::operator=( const FormulaTokenArray& rArr )
646 {
647     if(this == &rArr)
648         return *this;
649 
650     Clear();
651     Assign( rArr );
652     return *this;
653 }
654 
Clear()655 void FormulaTokenArray::Clear()
656 {
657     if( nRPN ) DelRPN();
658     if( pCode )
659     {
660         FormulaToken** p = pCode.get();
661         for( sal_uInt16 i = 0; i < nLen; i++ )
662         {
663             (*p++)->DecRef();
664         }
665         pCode.reset();
666     }
667     pRPN = nullptr;
668     nError = FormulaError::NONE;
669     nLen = nRPN = 0;
670     bHyperLink = false;
671     mbFromRangeName = false;
672     mbShareable = true;
673     mbFinalized = false;
674     ClearRecalcMode();
675 }
676 
CheckToken(const FormulaToken &)677 void FormulaTokenArray::CheckToken( const FormulaToken& /*r*/ )
678 {
679     // Do nothing.
680 }
681 
CheckAllRPNTokens()682 void FormulaTokenArray::CheckAllRPNTokens()
683 {
684     if( nRPN )
685     {
686         FormulaToken** p = pRPN;
687         for( sal_uInt16 i = 0; i < nRPN; i++ )
688         {
689             CheckToken( *p[ i ] );
690         }
691     }
692 }
693 
AddToken(const FormulaToken & r)694 FormulaToken* FormulaTokenArray::AddToken( const FormulaToken& r )
695 {
696     return Add( r.Clone() );
697 }
698 
MergeArray()699 FormulaToken* FormulaTokenArray::MergeArray( )
700 {
701     return nullptr;
702 }
703 
ReplaceToken(sal_uInt16 nOffset,FormulaToken * t,FormulaTokenArray::ReplaceMode eMode)704 FormulaToken* FormulaTokenArray::ReplaceToken( sal_uInt16 nOffset, FormulaToken* t,
705         FormulaTokenArray::ReplaceMode eMode )
706 {
707     if (nOffset < nLen)
708     {
709         CheckToken(*t);
710         t->IncRef();
711         FormulaToken* p = pCode[nOffset];
712         pCode[nOffset] = t;
713         if (eMode == CODE_AND_RPN && p->GetRef() > 1)
714         {
715             for (sal_uInt16 i=0; i < nRPN; ++i)
716             {
717                 if (pRPN[i] == p)
718                 {
719                     t->IncRef();
720                     pRPN[i] = t;
721                     p->DecRef();
722                     if (p->GetRef() == 1)
723                         break;  // for
724                 }
725             }
726         }
727         p->DecRef();    // may be dead now
728         return t;
729     }
730     else
731     {
732         t->DeleteIfZeroRef();
733         return nullptr;
734     }
735 }
736 
RemoveToken(sal_uInt16 nOffset,sal_uInt16 nCount)737 sal_uInt16 FormulaTokenArray::RemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
738 {
739     if (nOffset < nLen)
740     {
741         SAL_WARN_IF( nOffset + nCount > nLen, "formula.core",
742                 "FormulaTokenArray::RemoveToken - nOffset " << nOffset << " + nCount " << nCount << " > nLen " << nLen);
743         const sal_uInt16 nStop = ::std::min( static_cast<sal_uInt16>(nOffset + nCount), nLen);
744         nCount = nStop - nOffset;
745         for (sal_uInt16 j = nOffset; j < nStop; ++j)
746         {
747             FormulaToken* p = pCode[j];
748             if (p->GetRef() > 1)
749             {
750                 for (sal_uInt16 i=0; i < nRPN; ++i)
751                 {
752                     if (pRPN[i] == p)
753                     {
754                         // Shift remaining tokens in pRPN down.
755                         for (sal_uInt16 x=i+1; x < nRPN; ++x)
756                         {
757                             pRPN[x-1] = pRPN[x];
758                         }
759                         --nRPN;
760 
761                         p->DecRef();
762                         if (p->GetRef() == 1)
763                             break;  // for
764                     }
765                 }
766             }
767             p->DecRef();    // may be dead now
768         }
769 
770         // Shift remaining tokens in pCode down.
771         for (sal_uInt16 x = nStop; x < nLen; ++x)
772         {
773             pCode[x-nCount] = pCode[x];
774         }
775         nLen -= nCount;
776 
777         return nCount;
778     }
779     else
780     {
781         SAL_WARN("formula.core","FormulaTokenArray::RemoveToken - nOffset " << nOffset << " >= nLen " << nLen);
782         return 0;
783     }
784 }
785 
Add(FormulaToken * t)786 FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
787 {
788     assert(!mbFinalized);
789     if (mbFinalized)
790     {
791         t->DeleteIfZeroRef();
792         return nullptr;
793     }
794 
795 // Allocating an array of size FORMULA_MAXTOKENS is simple, but that results in relatively large
796 // allocations that malloc() implementations usually do not handle as efficiently as smaller
797 // sizes (not only in terms of memory usage but also speed). Since most token arrays are going
798 // to be small, start with a small array and resize only if needed. Eventually Finalize() will
799 // reallocate the memory to size exactly matching the requirements.
800     const size_t MAX_FAST_TOKENS = 32;
801     if( !pCode )
802         pCode.reset(new FormulaToken*[ MAX_FAST_TOKENS ]);
803     if( nLen == MAX_FAST_TOKENS )
804     {
805         FormulaToken** tmp = new FormulaToken*[ FORMULA_MAXTOKENS ];
806         std::copy(&pCode[0], &pCode[MAX_FAST_TOKENS], tmp);
807         pCode.reset(tmp);
808     }
809     if( nLen < FORMULA_MAXTOKENS - 1 )
810     {
811         CheckToken(*t);
812         pCode[ nLen++ ] = t;
813         t->IncRef();
814         if( t->GetOpCode() == ocArrayClose )
815             return MergeArray();
816         return t;
817     }
818     else
819     {
820         t->DeleteIfZeroRef();
821         if ( nLen == FORMULA_MAXTOKENS - 1 )
822         {
823             t = new FormulaByteToken( ocStop );
824             pCode[ nLen++ ] = t;
825             t->IncRef();
826         }
827         return nullptr;
828     }
829 }
830 
AddString(const svl::SharedString & rStr)831 FormulaToken* FormulaTokenArray::AddString( const svl::SharedString& rStr )
832 {
833     return Add( new FormulaStringToken( rStr ) );
834 }
835 
AddDouble(double fVal)836 FormulaToken* FormulaTokenArray::AddDouble( double fVal )
837 {
838     return Add( new FormulaDoubleToken( fVal ) );
839 }
840 
AddExternal(const sal_Unicode * pStr)841 void FormulaTokenArray::AddExternal( const sal_Unicode* pStr )
842 {
843     AddExternal( OUString( pStr ) );
844 }
845 
AddExternal(const OUString & rStr,OpCode eOp)846 FormulaToken* FormulaTokenArray::AddExternal( const OUString& rStr,
847         OpCode eOp /* = ocExternal */ )
848 {
849     return Add( new FormulaExternalToken( eOp, rStr ) );
850 }
851 
AddBad(const OUString & rStr)852 FormulaToken* FormulaTokenArray::AddBad( const OUString& rStr )
853 {
854     return Add( new FormulaStringOpToken( ocBad, svl::SharedString( rStr ) ) ); // string not interned
855 }
856 
AddStringXML(const OUString & rStr)857 FormulaToken* FormulaTokenArray::AddStringXML( const OUString& rStr )
858 {
859     return Add( new FormulaStringOpToken( ocStringXML, svl::SharedString( rStr ) ) );   // string not interned
860 }
861 
862 
AddRecalcMode(ScRecalcMode nBits)863 void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
864 {
865     const unsigned nExclusive = static_cast<sal_uInt8>(nBits & ScRecalcMode::EMask);
866     if (nExclusive)
867     {
868         unsigned nExBit;
869         if (nExclusive & (nExclusive - 1))
870         {
871             // More than one bit set, use highest priority.
872             for (nExBit = 1; (nExBit & static_cast<sal_uInt8>(ScRecalcMode::EMask)) != 0; nExBit <<= 1)
873             {
874                 if (nExclusive & nExBit)
875                     break;
876             }
877         }
878         else
879         {
880             // Only one bit is set.
881             nExBit = nExclusive;
882         }
883         // Set exclusive bit if priority is higher than existing.
884         if (nExBit < static_cast<sal_uInt8>(nMode & ScRecalcMode::EMask))
885             SetMaskedRecalcMode( static_cast<ScRecalcMode>(nExBit));
886     }
887     SetCombinedBitsRecalcMode( nBits );
888 }
889 
890 
HasMatrixDoubleRefOps() const891 bool FormulaTokenArray::HasMatrixDoubleRefOps() const
892 {
893     if ( pRPN && nRPN )
894     {
895         // RPN-Interpreter simulation.
896         // Simply assumes a double as return value of each function.
897         std::unique_ptr<FormulaToken*[]> pStack(new FormulaToken* [nRPN]);
898         FormulaToken* pResult = new FormulaDoubleToken( 0.0 );
899         short sp = 0;
900         for ( auto t: RPNTokens() )
901         {
902             OpCode eOp = t->GetOpCode();
903             sal_uInt8 nParams = t->GetParamCount();
904             switch ( eOp )
905             {
906                 case ocAdd :
907                 case ocSub :
908                 case ocMul :
909                 case ocDiv :
910                 case ocPow :
911                 case ocPower :
912                 case ocAmpersand :
913                 case ocEqual :
914                 case ocNotEqual :
915                 case ocLess :
916                 case ocGreater :
917                 case ocLessEqual :
918                 case ocGreaterEqual :
919                 {
920                     for ( sal_uInt8 k = nParams; k; k-- )
921                     {
922                         if ( sp >= k && pStack[sp-k]->GetType() == svDoubleRef )
923                         {
924                             pResult->Delete();
925                             return true;
926                         }
927                     }
928                 }
929                 break;
930                 default:
931                 {
932                     // added to avoid warnings
933                 }
934             }
935             if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() )  )
936                 pStack[sp++] = t;
937             else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
938             {   // ignore Jumps, pop previous Result (Condition)
939                 if ( sp )
940                     --sp;
941             }
942             else
943             {   // pop parameters, push result
944                 sp = sal::static_int_cast<short>( sp - nParams );
945                 if ( sp < 0 )
946                 {
947                     SAL_WARN("formula.core", "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" );
948                     sp = 0;
949                 }
950                 pStack[sp++] = pResult;
951             }
952         }
953         pResult->Delete();
954     }
955 
956     return false;
957 }
958 
959 // --- Formula rewrite of a token array
960 
isRewriteNeeded(OpCode eOp) const961 inline bool MissingConventionODF::isRewriteNeeded( OpCode eOp ) const
962 {
963     switch (eOp)
964     {
965         case ocGammaDist:
966         case ocPoissonDist:
967         case ocAddress:
968         case ocLogInv:
969         case ocLogNormDist:
970         case ocNormDist:
971             return true;
972         case ocMissing:
973         case ocLog:
974             return isPODF();    // rewrite only for PODF
975         case ocDBCount:
976         case ocDBCount2:
977             return isODFF();    // rewrite only for ODFF
978         default:
979             return false;
980     }
981 }
982 
983 /*
984  fdo 81596
985 To be implemented yet:
986   ocExternal:    ?
987   ocMacro:       ?
988   ocIndex:       INDEX() ?
989 */
isRewriteNeeded(OpCode eOp)990 inline bool MissingConventionOOXML::isRewriteNeeded( OpCode eOp )
991 {
992     switch (eOp)
993     {
994         case ocIf:
995 
996         case ocExternal:
997         case ocEuroConvert:
998         case ocMacro:
999 
1000         case ocRound:
1001         case ocRoundUp:
1002         case ocRoundDown:
1003 
1004         case ocIndex:
1005 
1006         case ocCeil:
1007         case ocFloor:
1008 
1009         case ocGammaDist:
1010         case ocFDist_LT:
1011         case ocPoissonDist:
1012         case ocNormDist:
1013         case ocLogInv:
1014         case ocLogNormDist:
1015         case ocHypGeomDist:
1016 
1017         case ocDBCount:
1018         case ocDBCount2:
1019             return true;
1020 
1021         default:
1022             return false;
1023     }
1024 }
1025 
1026 namespace {
1027 
1028 class FormulaMissingContext
1029 {
1030     public:
1031             const FormulaToken* mpFunc;
1032             int                 mnCurArg;
1033 
Clear()1034                     void    Clear() { mpFunc = nullptr; mnCurArg = 0; }
1035             inline  bool    AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const;
1036                     bool    AddMissingExternal( FormulaTokenArray* pNewArr ) const;
1037                     bool    AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const;
1038                     void    AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const;
1039 };
1040 
1041 }
1042 
AddMoreArgs(FormulaTokenArray * pNewArr,const MissingConvention & rConv) const1043 void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const
1044 {
1045     if ( !mpFunc )
1046         return;
1047 
1048     switch (rConv.getConvention())
1049     {
1050         case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
1051         case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
1052             {
1053                 switch (mpFunc->GetOpCode())
1054                 {
1055                     case ocGammaDist:
1056                         if (mnCurArg == 2)
1057                         {
1058                             pNewArr->AddOpCode( ocSep );
1059                             pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
1060                         }
1061                         break;
1062                     case ocPoissonDist:
1063                         if (mnCurArg == 1)
1064                         {
1065                             pNewArr->AddOpCode( ocSep );
1066                             pNewArr->AddDouble( 1.0 );      // 3rd, Cumulative=true()
1067                         }
1068                         break;
1069                     case ocNormDist:
1070                         if ( mnCurArg == 2 )
1071                         {
1072                             pNewArr->AddOpCode( ocSep );
1073                             pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
1074                         }
1075                         break;
1076                     case ocLogInv:
1077                     case ocLogNormDist:
1078                         if ( mnCurArg == 0 )
1079                         {
1080                             pNewArr->AddOpCode( ocSep );
1081                             pNewArr->AddDouble( 0.0 );      // 2nd, mean = 0.0
1082                         }
1083                         if ( mnCurArg <= 1 )
1084                         {
1085                             pNewArr->AddOpCode( ocSep );
1086                             pNewArr->AddDouble( 1.0 );      // 3rd, standard deviation = 1.0
1087                         }
1088                         break;
1089                     case ocLog:
1090                         if ( rConv.isPODF() && mnCurArg == 0 )
1091                         {
1092                             pNewArr->AddOpCode( ocSep );
1093                             pNewArr->AddDouble( 10.0 );     // 2nd, basis 10
1094                         }
1095                         break;
1096                     default:
1097                         break;
1098                 }
1099             }
1100             break;
1101         case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
1102             {
1103                 switch (mpFunc->GetOpCode())
1104                 {
1105                     case ocIf:
1106                         if( mnCurArg == 0 )
1107                         {
1108                             // Excel needs at least two parameters in IF function
1109                             pNewArr->AddOpCode( ocSep );
1110                             pNewArr->AddOpCode( ocTrue );   // 2nd, true() as function
1111                             pNewArr->AddOpCode( ocOpen );   // so the result is of logical type
1112                             pNewArr->AddOpCode( ocClose );  // and survives roundtrip
1113                         }
1114                         break;
1115 
1116                     case ocEuroConvert:
1117                         if ( mnCurArg == 2 )
1118                         {
1119                             pNewArr->AddOpCode( ocSep );
1120                             pNewArr->AddDouble( 0.0 );      // 4th, FullPrecision = false()
1121                         }
1122                         break;
1123 
1124                     case ocPoissonDist:
1125                         if (mnCurArg == 1)
1126                         {
1127                             pNewArr->AddOpCode( ocSep );
1128                             pNewArr->AddDouble( 1.0 );      // 3rd, Cumulative=true()
1129                         }
1130                         break;
1131 
1132                     case ocGammaDist:
1133                     case ocFDist_LT:
1134                     case ocNormDist:
1135                         if (mnCurArg == 2)
1136                         {
1137                             pNewArr->AddOpCode( ocSep );
1138                             pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
1139                         }
1140                         break;
1141 
1142                     case ocLogInv:
1143                     case ocLogNormDist:
1144                         if ( mnCurArg == 0 )
1145                         {
1146                             pNewArr->AddOpCode( ocSep );
1147                             pNewArr->AddDouble( 0.0 );      // 2nd, mean = 0.0
1148                         }
1149                         if ( mnCurArg <= 1 )
1150                         {
1151                             pNewArr->AddOpCode( ocSep );
1152                             pNewArr->AddDouble( 1.0 );      // 3rd, standard deviation = 1.0
1153                         }
1154                         break;
1155 
1156                     case ocHypGeomDist:
1157                         if ( mnCurArg == 3 )
1158                         {
1159                             pNewArr->AddOpCode( ocSep );
1160                             pNewArr->AddDouble( 0.0 );      // 5th, Cumulative = false()
1161                         }
1162                         break;
1163 
1164                     case ocRound:
1165                     case ocRoundUp:
1166                     case ocRoundDown:
1167                         if( mnCurArg == 0 )
1168                         {
1169                             // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
1170                             pNewArr->AddOpCode( ocSep );
1171                             pNewArr->AddDouble( 0.0 );      // 2nd, 0.0
1172                         }
1173                         break;
1174 
1175                     default:
1176                         break;
1177                 }
1178             }
1179             break;
1180     }
1181 
1182 }
1183 
AddDefaultArg(FormulaTokenArray * pNewArr,int nArg,double f) const1184 inline bool FormulaMissingContext::AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const
1185 {
1186     if (mnCurArg == nArg)
1187     {
1188         pNewArr->AddDouble( f );
1189         return true;
1190     }
1191     return false;
1192 }
1193 
AddMissingExternal(FormulaTokenArray * pNewArr) const1194 bool FormulaMissingContext::AddMissingExternal( FormulaTokenArray *pNewArr ) const
1195 {
1196     // Only called for PODF, not ODFF. No need to distinguish.
1197 
1198     const OUString &rName = mpFunc->GetExternal();
1199 
1200     // initial (fast) check:
1201     sal_Unicode nLastChar = rName[ rName.getLength() - 1];
1202     if ( nLastChar != 't' && nLastChar != 'm' )
1203         return false;
1204 
1205     if (rName.equalsIgnoreAsciiCase(
1206                 "com.sun.star.sheet.addin.Analysis.getAccrint" ))
1207     {
1208         return AddDefaultArg( pNewArr, 4, 1000.0 );
1209     }
1210     if (rName.equalsIgnoreAsciiCase(
1211                 "com.sun.star.sheet.addin.Analysis.getAccrintm" ))
1212     {
1213         return AddDefaultArg( pNewArr, 3, 1000.0 );
1214     }
1215     return false;
1216 }
1217 
AddMissing(FormulaTokenArray * pNewArr,const MissingConvention & rConv) const1218 bool FormulaMissingContext::AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const
1219 {
1220     if ( !mpFunc )
1221         return false;
1222 
1223     bool bRet = false;
1224     const OpCode eOp = mpFunc->GetOpCode();
1225 
1226     switch (rConv.getConvention())
1227     {
1228         case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
1229             {
1230                 // Add for ODFF
1231                 switch (eOp)
1232                 {
1233                     case ocAddress:
1234                         return AddDefaultArg( pNewArr, 2, 1.0 );    // abs
1235                     default:
1236                         break;
1237                 }
1238             }
1239             break;
1240         case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
1241             {
1242                 // Add for PODF
1243                 switch (eOp)
1244                 {
1245                     case ocAddress:
1246                         return AddDefaultArg( pNewArr, 2, 1.0 );    // abs
1247                     case ocFixed:
1248                         return AddDefaultArg( pNewArr, 1, 2.0 );
1249                     case ocBetaDist:
1250                     case ocBetaInv:
1251                     case ocPMT:
1252                         return AddDefaultArg( pNewArr, 3, 0.0 );
1253                     case ocIpmt:
1254                     case ocPpmt:
1255                         return AddDefaultArg( pNewArr, 4, 0.0 );
1256                     case ocPV:
1257                     case ocFV:
1258                         bRet |= AddDefaultArg( pNewArr, 2, 0.0 );   // pmt
1259                         bRet |= AddDefaultArg( pNewArr, 3, 0.0 );   // [fp]v
1260                         break;
1261                     case ocRate:
1262                         bRet |= AddDefaultArg( pNewArr, 1, 0.0 );   // pmt
1263                         bRet |= AddDefaultArg( pNewArr, 3, 0.0 );   // fv
1264                         bRet |= AddDefaultArg( pNewArr, 4, 0.0 );   // type
1265                         break;
1266                     case ocExternal:
1267                         return AddMissingExternal( pNewArr );
1268 
1269                         // --- more complex cases ---
1270 
1271                     case ocOffset:
1272                         // FIXME: rather tough
1273                         // if arg 3 (height) omitted, export arg1 (rows)
1274                         break;
1275                     default:
1276                         break;
1277                 }
1278             }
1279             break;
1280         case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
1281             {
1282                 switch (eOp)
1283                 {
1284                     case ocExternal:
1285                         return AddMissingExternal( pNewArr );
1286                     default:
1287                         break;
1288                 }
1289             }
1290             break;
1291     }
1292 
1293     return bRet;
1294 }
1295 
NeedsPodfRewrite(const MissingConventionODF & rConv)1296 bool FormulaTokenArray::NeedsPodfRewrite( const MissingConventionODF & rConv )
1297 {
1298     for ( auto i: Tokens()  )
1299     {
1300         if ( rConv.isRewriteNeeded( i->GetOpCode()))
1301             return true;
1302     }
1303     return false;
1304 }
1305 
NeedsOoxmlRewrite()1306 bool FormulaTokenArray::NeedsOoxmlRewrite()
1307 {
1308     for ( auto i: Tokens() )
1309     {
1310         if ( MissingConventionOOXML::isRewriteNeeded( i->GetOpCode()))
1311             return true;
1312     }
1313     return false;
1314 }
1315 
1316 
RewriteMissing(const MissingConvention & rConv)1317 FormulaTokenArray * FormulaTokenArray::RewriteMissing( const MissingConvention & rConv )
1318 {
1319     const size_t nAlloc = 256;
1320     FormulaMissingContext aCtx[ nAlloc ];
1321 
1322     /* TODO: with some effort we might be able to merge the two almost
1323      * identical function stacks into one and generalize things, otherwise
1324      * adding yet another "omit argument" would be copypasta. */
1325 
1326     int aOpCodeAddressStack[ nAlloc ];  // use of ADDRESS() function
1327     const int nOmitAddressArg = 3;      // ADDRESS() 4th parameter A1/R1C1
1328 
1329     int aOpCodeDcountStack[ nAlloc ];   // use of DCOUNT()/DCOUNTA() function
1330     const int nOmitDcountArg = 1;       // DCOUNT() and DCOUNTA() 2nd parameter DatabaseField if 0
1331 
1332     sal_uInt16 nTokens = GetLen() + 1;
1333     FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]);
1334     int* pOcas = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeAddressStack[0]);
1335     int* pOcds = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeDcountStack[0]);
1336     // Never go below 0, never use 0, mpFunc always NULL.
1337     pCtx[0].Clear();
1338     int nFn = 0;
1339     int nOcas = 0;
1340     int nOcds = 0;
1341 
1342     FormulaTokenArray *pNewArr = new FormulaTokenArray;
1343     // At least ScRecalcMode::ALWAYS needs to be set.
1344     pNewArr->AddRecalcMode( GetRecalcMode());
1345 
1346     FormulaTokenArrayPlainIterator aIter(*this);
1347     for ( FormulaToken *pCur = aIter.First(); pCur; pCur = aIter.Next() )
1348     {
1349         bool bAdd = true;
1350         // Don't write the expression of the new inserted ADDRESS() parameter.
1351         // Do NOT omit the new second parameter of INDIRECT() though. If that
1352         // was done for both, INDIRECT() actually could calculate different and
1353         // valid (but wrong) results with the then changed return value of
1354         // ADDRESS(). Better let it generate an error instead.
1355         for (int i = nOcas; i-- > 0 && bAdd; )
1356         {
1357             if (pCtx[ pOcas[ i ] ].mnCurArg == nOmitAddressArg)
1358             {
1359                 // Omit everything except a trailing separator, the leading
1360                 // separator is omitted below. The other way around would leave
1361                 // an extraneous separator if no parameter followed.
1362                 if (pOcas[ i ] != nFn || pCur->GetOpCode() != ocSep)
1363                     bAdd = false;
1364             }
1365         }
1366         // Strip the 2nd argument (leaving empty) of DCOUNT() and DCOUNTA() if
1367         // it is 0.
1368         for (int i = nOcds; i-- > 0 && bAdd; )
1369         {
1370             if (pCtx[ pOcds[ i ] ].mnCurArg == nOmitDcountArg)
1371             {
1372                 // Omit only a literal 0 value, nothing else.
1373                 if (pOcds[ i ] == nFn && pCur->GetOpCode() == ocPush && pCur->GetType() == svDouble &&
1374                         pCur->GetDouble() == 0.0)
1375                 {
1376                     // No other expression, between separators.
1377                     FormulaToken* p = aIter.PeekPrevNoSpaces();
1378                     if (p && p->GetOpCode() == ocSep)
1379                     {
1380                         p = aIter.PeekNextNoSpaces();
1381                         if (p && p->GetOpCode() == ocSep)
1382                             bAdd = false;
1383                     }
1384                 }
1385             }
1386         }
1387         switch ( pCur->GetOpCode() )
1388         {
1389             case ocOpen:
1390                 {
1391                     ++nFn;      // all following operations on _that_ function
1392                     pCtx[ nFn ].mpFunc = aIter.PeekPrevNoSpaces();
1393                     pCtx[ nFn ].mnCurArg = 0;
1394                     if (rConv.isPODF() && pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress)
1395                         pOcas[ nOcas++ ] = nFn;     // entering ADDRESS() if PODF
1396                     else if ((rConv.isODFF() || rConv.isOOXML()) && pCtx[ nFn ].mpFunc)
1397                     {
1398                         OpCode eOp = pCtx[ nFn ].mpFunc->GetOpCode();
1399                         if (eOp == ocDBCount || eOp == ocDBCount2)
1400                             pOcds[ nOcds++ ] = nFn;     // entering DCOUNT() or DCOUNTA() if ODFF or OOXML
1401                     }
1402                 }
1403             break;
1404             case ocClose:
1405                 pCtx[ nFn ].AddMoreArgs( pNewArr, rConv );
1406                 SAL_WARN_IF(nFn <= 0, "formula.core", "FormulaTokenArray::RewriteMissing: underflow");
1407                 if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn)
1408                     --nOcas;                    // leaving ADDRESS()
1409                 else if (nOcds > 0 && pOcds[ nOcds-1 ] == nFn)
1410                     --nOcds;                    // leaving DCOUNT() or DCOUNTA()
1411                 if (nFn > 0)
1412                     --nFn;
1413                 break;
1414             case ocSep:
1415                 pCtx[ nFn ].mnCurArg++;
1416                 // Omit leading separator of ADDRESS() parameter.
1417                 if (nOcas && pOcas[ nOcas-1 ] == nFn && pCtx[ nFn ].mnCurArg == nOmitAddressArg)
1418                 {
1419                     bAdd = false;
1420                 }
1421                 break;
1422             case ocMissing:
1423                 if ( bAdd )
1424                     bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv );
1425                 break;
1426             default:
1427                 break;
1428         }
1429         if (bAdd)
1430         {
1431             OpCode eOp = pCur->GetOpCode();
1432             if ( ( eOp == ocCeil || eOp == ocFloor ||
1433                    ( eOp == ocLogNormDist && pCur->GetByte() == 4 ) ) &&
1434                  rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
1435             {
1436                 switch ( eOp )
1437                 {
1438                     case ocCeil :
1439                         eOp = ocCeil_Math;
1440                         break;
1441                     case ocFloor :
1442                         eOp = ocFloor_Math;
1443                         break;
1444                     case ocLogNormDist :
1445                         eOp = ocLogNormDist_MS;
1446                         break;
1447                     default :
1448                         eOp = ocNone;
1449                         break;
1450                 }
1451                 FormulaToken *pToken = new FormulaToken( svByte, eOp );
1452                 pNewArr->Add( pToken );
1453             }
1454             else if ( eOp == ocHypGeomDist &&
1455                       rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
1456             {
1457                 FormulaToken *pToken = new FormulaToken( svByte, ocHypGeomDist_MS );
1458                 pNewArr->Add( pToken );
1459             }
1460             else
1461                 pNewArr->AddToken( *pCur );
1462         }
1463     }
1464 
1465     if (pOcds != &aOpCodeDcountStack[0])
1466         delete [] pOcds;
1467     if (pOcas != &aOpCodeAddressStack[0])
1468         delete [] pOcas;
1469     if (pCtx != &aCtx[0])
1470         delete [] pCtx;
1471 
1472     return pNewArr;
1473 }
1474 
MayReferenceFollow()1475 bool FormulaTokenArray::MayReferenceFollow()
1476 {
1477     if ( pCode && nLen > 0 )
1478     {
1479         // ignore trailing spaces
1480         sal_uInt16 i = nLen - 1;
1481         while ( i > 0 && pCode[i]->GetOpCode() == SC_OPCODE_SPACES )
1482         {
1483             --i;
1484         }
1485         if ( i > 0 || pCode[i]->GetOpCode() != SC_OPCODE_SPACES )
1486         {
1487             OpCode eOp = pCode[i]->GetOpCode();
1488             if ( (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) ||
1489                  (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP ) ||
1490                  eOp == SC_OPCODE_OPEN || eOp == SC_OPCODE_SEP )
1491             {
1492                 return true;
1493             }
1494         }
1495     }
1496     return false;
1497 }
AddOpCode(OpCode eOp)1498 FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
1499 {
1500     FormulaToken* pRet = nullptr;
1501     switch ( eOp )
1502     {
1503         case ocOpen:
1504         case ocClose:
1505         case ocSep:
1506         case ocArrayOpen:
1507         case ocArrayClose:
1508         case ocArrayRowSep:
1509         case ocArrayColSep:
1510             pRet = new FormulaToken( svSep,eOp );
1511             break;
1512         case ocIf:
1513         case ocIfError:
1514         case ocIfNA:
1515         case ocChoose:
1516             {
1517                 short nJump[FORMULA_MAXJUMPCOUNT + 1];
1518                 if ( eOp == ocIf )
1519                     nJump[ 0 ] = 3;
1520                 else if ( eOp == ocChoose )
1521                     nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
1522                 else
1523                     nJump[ 0 ] = 2;
1524                 pRet = new FormulaJumpToken( eOp, nJump );
1525             }
1526             break;
1527         default:
1528             pRet = new FormulaByteToken( eOp, 0, ParamClass::Unknown );
1529             break;
1530     }
1531     return Add( pRet );
1532 }
1533 
ReinternStrings(svl::SharedStringPool & rPool)1534 void FormulaTokenArray::ReinternStrings( svl::SharedStringPool& rPool )
1535 {
1536     for (auto i: Tokens())
1537     {
1538         switch (i->GetType())
1539         {
1540             case svString:
1541                 i->SetString( rPool.intern( i->GetString().getString()));
1542                 break;
1543             default:
1544                 ;   // nothing
1545         }
1546     }
1547 }
1548 
1549 
1550 /*----------------------------------------------------------------------*/
1551 
Item(const FormulaTokenArray * pArray,short pc,short stop)1552 FormulaTokenIterator::Item::Item(const FormulaTokenArray* pArray, short pc, short stop) :
1553     pArr(pArray), nPC(pc), nStop(stop)
1554 {
1555 }
1556 
FormulaTokenIterator(const FormulaTokenArray & rArr)1557 FormulaTokenIterator::FormulaTokenIterator( const FormulaTokenArray& rArr )
1558 {
1559     Push( &rArr );
1560 }
1561 
~FormulaTokenIterator()1562 FormulaTokenIterator::~FormulaTokenIterator()
1563 {
1564 }
1565 
Push(const FormulaTokenArray * pArr)1566 void FormulaTokenIterator::Push( const FormulaTokenArray* pArr )
1567 {
1568     FormulaTokenIterator::Item item(pArr, -1, SHRT_MAX);
1569 
1570     maStack.push_back(item);
1571 }
1572 
Pop()1573 void FormulaTokenIterator::Pop()
1574 {
1575     maStack.pop_back();
1576 }
1577 
Reset()1578 void FormulaTokenIterator::Reset()
1579 {
1580     while( maStack.size() > 1 )
1581         maStack.pop_back();
1582 
1583     maStack.back().nPC = -1;
1584 }
1585 
GetNextName()1586 FormulaToken* FormulaTokenArrayPlainIterator::GetNextName()
1587 {
1588     if( mpFTA->GetArray() )
1589     {
1590         while ( mnIndex < mpFTA->GetLen() )
1591         {
1592             FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
1593             if( t->GetType() == svIndex )
1594                 return t;
1595         }
1596     }
1597     return nullptr;
1598 }
1599 
Next()1600 const FormulaToken* FormulaTokenIterator::Next()
1601 {
1602     const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC );
1603     if( !t && maStack.size() > 1 )
1604     {
1605         Pop();
1606         t = Next();
1607     }
1608     return t;
1609 }
1610 
PeekNextOperator()1611 const FormulaToken* FormulaTokenIterator::PeekNextOperator()
1612 {
1613     const FormulaToken* t = nullptr;
1614     short nIdx = maStack.back().nPC;
1615     for (;;)
1616     {
1617         t = GetNonEndOfPathToken( ++nIdx);
1618         if (t == nullptr || t->GetOpCode() != ocPush)
1619             break;   // ignore operands
1620     }
1621     if (!t && maStack.size() > 1)
1622     {
1623         FormulaTokenIterator::Item aHere = maStack.back();
1624         maStack.pop_back();
1625         t = PeekNextOperator();
1626         maStack.push_back(aHere);
1627     }
1628     return t;
1629 }
1630 
1631 //! The nPC counts after a Push() are -1
1632 
Jump(short nStart,short nNext,short nStop)1633 void FormulaTokenIterator::Jump( short nStart, short nNext, short nStop )
1634 {
1635     maStack.back().nPC = nNext;
1636     if( nStart != nNext )
1637     {
1638         Push( maStack.back().pArr );
1639         maStack.back().nPC = nStart;
1640         maStack.back().nStop = nStop;
1641     }
1642 }
1643 
ReInit(const FormulaTokenArray & rArr)1644 void FormulaTokenIterator::ReInit( const FormulaTokenArray& rArr )
1645 {
1646     maStack.clear();
1647     Push( &rArr );
1648 }
1649 
GetNonEndOfPathToken(short nIdx) const1650 const FormulaToken* FormulaTokenIterator::GetNonEndOfPathToken( short nIdx ) const
1651 {
1652     FormulaTokenIterator::Item cur = maStack.back();
1653 
1654     if (nIdx < cur.pArr->GetCodeLen() && nIdx < cur.nStop)
1655     {
1656         const FormulaToken* t = cur.pArr->GetCode()[ nIdx ];
1657         // such an OpCode ends an IF() or CHOOSE() path
1658         return (t->GetOpCode() == ocSep || t->GetOpCode() == ocClose) ? nullptr : t;
1659     }
1660     return nullptr;
1661 }
1662 
IsEndOfPath() const1663 bool FormulaTokenIterator::IsEndOfPath() const
1664 {
1665     return GetNonEndOfPathToken( maStack.back().nPC + 1) == nullptr;
1666 }
1667 
GetNextReference()1668 FormulaToken* FormulaTokenArrayPlainIterator::GetNextReference()
1669 {
1670     while( mnIndex < mpFTA->GetLen() )
1671     {
1672         FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
1673         switch( t->GetType() )
1674         {
1675             case svSingleRef:
1676             case svDoubleRef:
1677             case svExternalSingleRef:
1678             case svExternalDoubleRef:
1679                 return t;
1680             default:
1681             {
1682                 // added to avoid warnings
1683             }
1684         }
1685     }
1686     return nullptr;
1687 }
1688 
GetNextColRowName()1689 FormulaToken* FormulaTokenArrayPlainIterator::GetNextColRowName()
1690 {
1691     while( mnIndex < mpFTA->GetLen() )
1692     {
1693         FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
1694         if ( t->GetOpCode() == ocColRowName )
1695             return t;
1696     }
1697     return nullptr;
1698 }
1699 
GetNextReferenceRPN()1700 FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceRPN()
1701 {
1702     while( mnIndex < mpFTA->GetCodeLen() )
1703     {
1704         FormulaToken* t = mpFTA->GetCode()[ mnIndex++ ];
1705         switch( t->GetType() )
1706         {
1707             case svSingleRef:
1708             case svDoubleRef:
1709             case svExternalSingleRef:
1710             case svExternalDoubleRef:
1711                 return t;
1712             default:
1713             {
1714                 // added to avoid warnings
1715             }
1716         }
1717     }
1718     return nullptr;
1719 }
1720 
GetNextReferenceOrName()1721 FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceOrName()
1722 {
1723     if( mpFTA->GetArray() )
1724     {
1725         while ( mnIndex < mpFTA->GetLen() )
1726         {
1727             FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
1728             switch( t->GetType() )
1729             {
1730                 case svSingleRef:
1731                 case svDoubleRef:
1732                 case svIndex:
1733                 case svExternalSingleRef:
1734                 case svExternalDoubleRef:
1735                 case svExternalName:
1736                     return t;
1737                 default:
1738                 {
1739                     // added to avoid warnings
1740                 }
1741              }
1742          }
1743      }
1744     return nullptr;
1745 }
1746 
Next()1747 FormulaToken* FormulaTokenArrayPlainIterator::Next()
1748 {
1749     if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
1750         return mpFTA->GetArray()[ mnIndex++ ];
1751     else
1752         return nullptr;
1753 }
1754 
NextNoSpaces()1755 FormulaToken* FormulaTokenArrayPlainIterator::NextNoSpaces()
1756 {
1757     if( mpFTA->GetArray() )
1758     {
1759         while( (mnIndex < mpFTA->GetLen()) && (mpFTA->GetArray()[ mnIndex ]->GetOpCode() == ocSpaces) )
1760             ++mnIndex;
1761         if( mnIndex < mpFTA->GetLen() )
1762             return mpFTA->GetArray()[ mnIndex++ ];
1763     }
1764     return nullptr;
1765 }
1766 
NextRPN()1767 FormulaToken* FormulaTokenArrayPlainIterator::NextRPN()
1768 {
1769     if( mpFTA->GetCode() && mnIndex < mpFTA->GetCodeLen() )
1770         return mpFTA->GetCode()[ mnIndex++ ];
1771     else
1772         return nullptr;
1773 }
1774 
PrevRPN()1775 FormulaToken* FormulaTokenArrayPlainIterator::PrevRPN()
1776 {
1777     if( mpFTA->GetCode() && mnIndex )
1778         return mpFTA->GetCode()[ --mnIndex ];
1779     else
1780         return nullptr;
1781 }
1782 
PeekNext()1783 FormulaToken* FormulaTokenArrayPlainIterator::PeekNext()
1784 {
1785     if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
1786         return mpFTA->GetArray()[ mnIndex ];
1787     else
1788         return nullptr;
1789 }
1790 
PeekNextNoSpaces() const1791 FormulaToken* FormulaTokenArrayPlainIterator::PeekNextNoSpaces() const
1792 {
1793     if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
1794     {
1795         sal_uInt16 j = mnIndex;
1796         while ( j < mpFTA->GetLen() && mpFTA->GetArray()[j]->GetOpCode() == ocSpaces )
1797             j++;
1798         if ( j < mpFTA->GetLen() )
1799             return mpFTA->GetArray()[ j ];
1800         else
1801             return nullptr;
1802     }
1803     else
1804         return nullptr;
1805 }
1806 
PeekPrevNoSpaces() const1807 FormulaToken* FormulaTokenArrayPlainIterator::PeekPrevNoSpaces() const
1808 {
1809     if( mpFTA->GetArray() && mnIndex > 1 )
1810     {
1811         sal_uInt16 j = mnIndex - 2;
1812         while ( mpFTA->GetArray()[j]->GetOpCode() == ocSpaces && j > 0 )
1813             j--;
1814         if ( j > 0 || mpFTA->GetArray()[j]->GetOpCode() != ocSpaces )
1815             return mpFTA->GetArray()[ j ];
1816         else
1817             return nullptr;
1818     }
1819     else
1820         return nullptr;
1821 }
1822 
AfterRemoveToken(sal_uInt16 nOffset,sal_uInt16 nCount)1823 void FormulaTokenArrayPlainIterator::AfterRemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
1824 {
1825     const sal_uInt16 nStop = std::min( static_cast<sal_uInt16>(nOffset + nCount), mpFTA->GetLen());
1826 
1827     if (mnIndex >= nOffset)
1828     {
1829         if (mnIndex < nStop)
1830             mnIndex = nOffset + 1;
1831         else
1832             mnIndex -= nStop - nOffset;
1833     }
1834 }
1835 
1836 // real implementations of virtual functions
1837 
1838 
GetDouble() const1839 double      FormulaDoubleToken::GetDouble() const            { return fDouble; }
GetDoubleAsReference()1840 double &    FormulaDoubleToken::GetDoubleAsReference()       { return fDouble; }
1841 
GetDoubleType() const1842 sal_Int16 FormulaDoubleToken::GetDoubleType() const
1843 {
1844     // This is a plain double value without type information, don't emit a
1845     // warning via FormulaToken::GetDoubleType().
1846     return 0;
1847 }
1848 
operator ==(const FormulaToken & r) const1849 bool FormulaDoubleToken::operator==( const FormulaToken& r ) const
1850 {
1851     return FormulaToken::operator==( r ) && fDouble == r.GetDouble();
1852 }
1853 
GetDoubleType() const1854 sal_Int16 FormulaTypedDoubleToken::GetDoubleType() const
1855 {
1856     return mnType;
1857 }
1858 
SetDoubleType(sal_Int16 nType)1859 void FormulaTypedDoubleToken::SetDoubleType( sal_Int16 nType )
1860 {
1861     mnType = nType;
1862 }
1863 
operator ==(const FormulaToken & r) const1864 bool FormulaTypedDoubleToken::operator==( const FormulaToken& r ) const
1865 {
1866     return FormulaDoubleToken::operator==( r ) && mnType == r.GetDoubleType();
1867 }
1868 
FormulaStringToken(const svl::SharedString & r)1869 FormulaStringToken::FormulaStringToken( const svl::SharedString& r ) :
1870     FormulaToken( svString ), maString( r )
1871 {
1872 }
1873 
FormulaStringToken(const FormulaStringToken & r)1874 FormulaStringToken::FormulaStringToken( const FormulaStringToken& r ) :
1875     FormulaToken( r ), maString( r.maString ) {}
1876 
Clone() const1877 FormulaToken* FormulaStringToken::Clone() const
1878 {
1879     return new FormulaStringToken(*this);
1880 }
1881 
GetString() const1882 const svl::SharedString & FormulaStringToken::GetString() const
1883 {
1884     return maString;
1885 }
1886 
SetString(const svl::SharedString & rStr)1887 void FormulaStringToken::SetString( const svl::SharedString& rStr )
1888 {
1889     maString = rStr;
1890 }
1891 
operator ==(const FormulaToken & r) const1892 bool FormulaStringToken::operator==( const FormulaToken& r ) const
1893 {
1894     return FormulaToken::operator==( r ) && maString == r.GetString();
1895 }
1896 
FormulaStringOpToken(OpCode e,const svl::SharedString & r)1897 FormulaStringOpToken::FormulaStringOpToken( OpCode e, const svl::SharedString& r ) :
1898     FormulaByteToken( e, 0, svString, ParamClass::Unknown ), maString( r ) {}
1899 
FormulaStringOpToken(const FormulaStringOpToken & r)1900 FormulaStringOpToken::FormulaStringOpToken( const FormulaStringOpToken& r ) :
1901     FormulaByteToken( r ), maString( r.maString ) {}
1902 
Clone() const1903 FormulaToken* FormulaStringOpToken::Clone() const
1904 {
1905     return new FormulaStringOpToken(*this);
1906 }
1907 
GetString() const1908 const svl::SharedString & FormulaStringOpToken::GetString() const
1909 {
1910     return maString;
1911 }
1912 
SetString(const svl::SharedString & rStr)1913 void FormulaStringOpToken::SetString( const svl::SharedString& rStr )
1914 {
1915     maString = rStr;
1916 }
1917 
operator ==(const FormulaToken & r) const1918 bool FormulaStringOpToken::operator==( const FormulaToken& r ) const
1919 {
1920     return FormulaByteToken::operator==( r ) && maString == r.GetString();
1921 }
1922 
GetIndex() const1923 sal_uInt16  FormulaIndexToken::GetIndex() const             { return nIndex; }
SetIndex(sal_uInt16 n)1924 void        FormulaIndexToken::SetIndex( sal_uInt16 n )     { nIndex = n; }
GetSheet() const1925 sal_Int16   FormulaIndexToken::GetSheet() const             { return mnSheet; }
SetSheet(sal_Int16 n)1926 void        FormulaIndexToken::SetSheet( sal_Int16 n )      { mnSheet = n; }
operator ==(const FormulaToken & r) const1927 bool FormulaIndexToken::operator==( const FormulaToken& r ) const
1928 {
1929     return FormulaToken::operator==( r ) && nIndex == r.GetIndex() &&
1930         mnSheet == r.GetSheet();
1931 }
GetExternal() const1932 const OUString& FormulaExternalToken::GetExternal() const       { return aExternal; }
operator ==(const FormulaToken & r) const1933 bool FormulaExternalToken::operator==( const FormulaToken& r ) const
1934 {
1935     return FormulaByteToken::operator==( r ) && aExternal == r.GetExternal();
1936 }
1937 
1938 
GetError() const1939 FormulaError      FormulaErrorToken::GetError() const             { return nError; }
SetError(FormulaError nErr)1940 void            FormulaErrorToken::SetError( FormulaError nErr )  { nError = nErr; }
operator ==(const FormulaToken & r) const1941 bool FormulaErrorToken::operator==( const FormulaToken& r ) const
1942 {
1943     return FormulaToken::operator==( r ) &&
1944         nError == static_cast< const FormulaErrorToken & >(r).GetError();
1945 }
GetDouble() const1946 double          FormulaMissingToken::GetDouble() const       { return 0.0; }
1947 
GetString() const1948 const svl::SharedString & FormulaMissingToken::GetString() const
1949 {
1950     return svl::SharedString::getEmptyString();
1951 }
1952 
operator ==(const FormulaToken & r) const1953 bool FormulaMissingToken::operator==( const FormulaToken& r ) const
1954 {
1955     return FormulaToken::operator==( r );
1956 }
1957 
1958 
operator ==(const FormulaToken & r) const1959 bool FormulaUnknownToken::operator==( const FormulaToken& r ) const
1960 {
1961     return FormulaToken::operator==( r );
1962 }
1963 
1964 
1965 } // formula
1966 
1967 
1968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1969