/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
 
#include <algorithm>
 
#include <string.h>
#include <limits.h>
#include <osl/diagnose.h>
#include <sal/log.hxx>
 
#include <com/sun/star/sheet/FormulaToken.hpp>
#include <formula/errorcodes.hxx>
#include <formula/token.hxx>
#include <formula/tokenarray.hxx>
#include <formula/FormulaCompiler.hxx>
#include <formula/compiler.hxx>
#include <svl/sharedstringpool.hxx>
#include <memory>
#include <utility>
 
namespace formula
{
    using namespace com::sun::star;
 
 
// --- helpers --------------------------------------------------------------
 
static bool lcl_IsReference( OpCode eOp, StackVar eType )
{
    return
        (eOp == ocPush && (eType == svSingleRef || eType == svDoubleRef))
        || (eOp == ocColRowNameAuto && eType == svDoubleRef)
        || (eOp == ocColRowName && eType == svSingleRef)
        || (eOp == ocMatRef && eType == svSingleRef)
        ;
}
 
// --- class FormulaToken --------------------------------------------------------
 
FormulaToken::FormulaToken( StackVar eTypeP, OpCode e ) :
    eOp(e), eType( eTypeP ), eRefCntPolicy(RefCntPolicy::ThreadSafe), mnRefCnt(0)
{
}
 
FormulaToken::FormulaToken( const FormulaToken& r ) :
    eOp(r.eOp), eType( r.eType ), eRefCntPolicy(RefCntPolicy::ThreadSafe), mnRefCnt(0)
{
}
 
FormulaToken::~FormulaToken()
{
}
 
bool FormulaToken::IsFunction() const
{
    return (eOp != ocPush && eOp != ocBad && eOp != ocColRowName &&
            eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea &&
            eOp != ocTableRef &&
           (GetByte() != 0                                                  // x parameters
        || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)   // no parameter
        || FormulaCompiler::IsOpCodeJumpCommand( eOp )                      // @ jump commands
        || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR)     // one parameter
        || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)     // x parameters (cByte==0 in
                                                                            // FuncAutoPilot)
        || eOp == ocMacro || eOp == ocExternal                  // macros, AddIns
        || eOp == ocAnd || eOp == ocOr                          // former binary, now x parameters
        || (eOp >= ocInternalBegin && eOp <= ocInternalEnd)     // internal
        ));
}
 
 
sal_uInt8 FormulaToken::GetParamCount() const
{
    if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro &&
         !FormulaCompiler::IsOpCodeJumpCommand( eOp ) &&
         eOp != ocPercentSign )
        return 0;       // parameters and specials
                        // ocIf... jump commands not for FAP, have cByte then
//2do: bool parameter whether FAP or not?
    else if (GetByte())
        return GetByte();   // all functions, also ocExternal and ocMacro
    else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP && eOp != ocAnd && eOp != ocOr)
        return 2;           // binary operators, compiler checked; OR and AND legacy but are functions
    else if ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP) || eOp == ocPercentSign)
        return 1;           // unary operators, compiler checked
    else if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
        return 0;           // no parameter
    else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
        return 1;           // only the condition counts as parameter
    else
        return 0;           // all the rest, no Parameter, or
                            // if so then it should be in cByte
}
 
bool FormulaToken::IsExternalRef() const
{
    bool bRet = false;
    switch (eType)
    {
        case svExternalSingleRef:
        case svExternalDoubleRef:
        case svExternalName:
            bRet = true;
            break;
        default:
            bRet = false;
            break;
    }
    return bRet;
}
 
bool FormulaToken::IsRef() const
{
    switch (eType)
    {
        case svSingleRef:
        case svDoubleRef:
        case svExternalSingleRef:
        case svExternalDoubleRef:
            return true;
        default:
            if (eOp == ocTableRef)
                return true;
    }
 
    return false;
}
 
bool FormulaToken::IsInForceArray() const
{
    ParamClass eParam = GetInForceArray();
    return eParam == ParamClass::ForceArray || eParam == ParamClass::ReferenceOrForceArray
        || eParam == ParamClass::ReferenceOrRefArray || eParam == ParamClass::ForceArrayReturn;
}
 
bool FormulaToken::operator==( const FormulaToken& rToken ) const
{
    // don't compare reference count!
    return  eType == rToken.eType && GetOpCode() == rToken.GetOpCode();
}
 
 
// --- virtual dummy methods -------------------------------------------------
 
sal_uInt8 FormulaToken::GetByte() const
{
    // ok to be called for any derived class
    return 0;
}
 
void FormulaToken::SetByte( sal_uInt8 )
{
    assert( !"virtual dummy called" );
}
 
ParamClass FormulaToken::GetInForceArray() const
{
    // ok to be called for any derived class
    return (eOp == ocPush && eType == svMatrix) ? ParamClass::ForceArrayReturn : ParamClass::Unknown;
}
 
void FormulaToken::SetInForceArray( ParamClass )
{
    assert( !"virtual dummy called" );
}
 
double FormulaToken::GetDouble() const
{
    // This Get is worth an assert.
    assert( !"virtual dummy called" );
    return 0.0;
}
 
void FormulaToken::SetDouble(double)
{
    // This Get is worth an assert.
    assert( !"virtual dummy called" );
}
 
sal_Int16 FormulaToken::GetDoubleType() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetDoubleType: virtual dummy called" );
    return 0;
}
 
void FormulaToken::SetDoubleType( sal_Int16 )
{
    assert( !"virtual dummy called" );
}
 
const svl::SharedString INVALID_STRING;
 
const svl::SharedString & FormulaToken::GetString() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetString: virtual dummy called" );
    return INVALID_STRING; // invalid string
}
 
void FormulaToken::SetString( const svl::SharedString& )
{
    assert( !"virtual dummy called" );
}
 
sal_uInt16 FormulaToken::GetIndex() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetIndex: virtual dummy called" );
    return 0;
}
 
void FormulaToken::SetIndex( sal_uInt16 )
{
    assert( !"virtual dummy called" );
}
 
sal_Int16 FormulaToken::GetSheet() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetSheet: virtual dummy called" );
    return -1;
}
 
void FormulaToken::SetSheet( sal_Int16 )
{
    assert( !"virtual dummy called" );
}
 
sal_Unicode FormulaToken::GetChar() const
{
    // This Get is worth an assert.
    assert( !"virtual dummy called" );
    return 0;
}
 
short* FormulaToken::GetJump() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetJump: virtual dummy called" );
    return nullptr;
}
 
 
const OUString& FormulaToken::GetExternal() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetExternal: virtual dummy called" );
    static  OUString              aDummyString;
    return aDummyString;
}
 
FormulaToken* FormulaToken::GetFAPOrigToken() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetFAPOrigToken: virtual dummy called" );
    return nullptr;
}
 
FormulaError FormulaToken::GetError() const
{
    SAL_WARN( "formula.core", "FormulaToken::GetError: virtual dummy called" );
    return FormulaError::NONE;
}
 
void FormulaToken::SetError( FormulaError )
{
    assert( !"virtual dummy called" );
}
 
const ScSingleRefData* FormulaToken::GetSingleRef() const
{
    OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
    return nullptr;
}
 
ScSingleRefData* FormulaToken::GetSingleRef()
{
    OSL_FAIL( "FormulaToken::GetSingleRef: virtual dummy called" );
    return nullptr;
}
 
const ScComplexRefData* FormulaToken::GetDoubleRef() const
{
    OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
    return nullptr;
}
 
ScComplexRefData* FormulaToken::GetDoubleRef()
{
    OSL_FAIL( "FormulaToken::GetDoubleRef: virtual dummy called" );
    return nullptr;
}
 
const ScSingleRefData* FormulaToken::GetSingleRef2() const
{
    OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
    return nullptr;
}
 
ScSingleRefData* FormulaToken::GetSingleRef2()
{
    OSL_FAIL( "FormulaToken::GetSingleRef2: virtual dummy called" );
    return nullptr;
}
 
const ScMatrix* FormulaToken::GetMatrix() const
{
    OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
    return nullptr;
}
 
ScMatrix* FormulaToken::GetMatrix()
{
    OSL_FAIL( "FormulaToken::GetMatrix: virtual dummy called" );
    return nullptr;
}
 
ScJumpMatrix* FormulaToken::GetJumpMatrix() const
{
    OSL_FAIL( "FormulaToken::GetJumpMatrix: virtual dummy called" );
    return nullptr;
}
const std::vector<ScComplexRefData>* FormulaToken::GetRefList() const
{
    OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
    return nullptr;
}
 
std::vector<ScComplexRefData>* FormulaToken::GetRefList()
{
    OSL_FAIL( "FormulaToken::GetRefList: virtual dummy called" );
    return nullptr;
}
 
bool FormulaToken::TextEqual( const FormulaToken& rToken ) const
{
    return *this == rToken;
}
 
// real implementations of virtual functions
 
 
sal_uInt8   FormulaSpaceToken::GetByte() const  { return nByte; }
sal_Unicode FormulaSpaceToken::GetChar() const  { return cChar; }
bool FormulaSpaceToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
        cChar == r.GetChar();
}
 
 
sal_uInt8   FormulaByteToken::GetByte() const           { return nByte; }
void        FormulaByteToken::SetByte( sal_uInt8 n )    { nByte = n; }
ParamClass  FormulaByteToken::GetInForceArray() const    { return eInForceArray; }
void        FormulaByteToken::SetInForceArray( ParamClass c ) { eInForceArray = c; }
bool FormulaByteToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && nByte == r.GetByte() &&
        eInForceArray == r.GetInForceArray();
}
 
 
FormulaToken* FormulaFAPToken::GetFAPOrigToken() const  { return pOrigToken.get(); }
bool FormulaFAPToken::operator==( const FormulaToken& r ) const
{
    return FormulaByteToken::operator==( r ) && pOrigToken == r.GetFAPOrigToken();
}
 
 
short*      FormulaJumpToken::GetJump() const                   { return pJump.get(); }
ParamClass  FormulaJumpToken::GetInForceArray() const           { return eInForceArray; }
void        FormulaJumpToken::SetInForceArray( ParamClass c )   { eInForceArray = c; }
bool FormulaJumpToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && pJump[0] == r.GetJump()[0] &&
        memcmp( pJump.get()+1, r.GetJump()+1, pJump[0] * sizeof(short) ) == 0 &&
        eInForceArray == r.GetInForceArray();
}
FormulaJumpToken::~FormulaJumpToken()
{
}
 
 
bool FormulaTokenArray::AddFormulaToken(
    const sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, ExternalReferenceHelper* /*pExtRef*/)
{
    bool bError = false;
    const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode);      //! assuming equal values for the moment
 
    const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
    switch ( eClass )
    {
        case uno::TypeClass_VOID:
            // empty data -> use AddOpCode (does some special cases)
            AddOpCode( eOpCode );
            break;
        case uno::TypeClass_DOUBLE:
            // double is only used for "push"
            if ( eOpCode == ocPush )
                AddDouble( rToken.Data.get<double>() );
            else
                bError = true;
            break;
        case uno::TypeClass_LONG:
            {
                // long is svIndex, used for name / database area, or "byte" for spaces
                sal_Int32 nValue = rToken.Data.get<sal_Int32>();
                if ( eOpCode == ocDBArea )
                    Add( new formula::FormulaIndexToken( eOpCode, static_cast<sal_uInt16>(nValue) ) );
                else if ( eOpCode == ocSpaces )
                    Add( new formula::FormulaByteToken( ocSpaces, static_cast<sal_uInt8>(nValue) ) );
                else
                    bError = true;
            }
            break;
        case uno::TypeClass_STRING:
            {
                OUString aStrVal( rToken.Data.get<OUString>() );
                if ( eOpCode == ocPush )
                    AddString(rSPool.intern(aStrVal));
                else if ( eOpCode == ocBad )
                    AddBad( aStrVal );
                else if ( eOpCode == ocStringXML )
                    AddStringXML( aStrVal );
                else if ( eOpCode == ocStringName )
                    AddStringName( aStrVal );
                else if ( eOpCode == ocExternal || eOpCode == ocMacro )
                    Add( new formula::FormulaExternalToken( eOpCode, aStrVal ) );
                else if ( eOpCode == ocWhitespace )
                {
                    // Simply ignore empty string.
                    // Convention is one character repeated.
                    if (!aStrVal.isEmpty())
                        Add( new formula::FormulaSpaceToken( static_cast<sal_uInt8>(aStrVal.getLength()), aStrVal[0]));
                }
                else
                    bError = true;      // unexpected string: don't know what to do with it
            }
            break;
        default:
            bError = true;
    } // switch ( eClass )
    return bError;
}
 
bool FormulaTokenArray::Fill(
    const uno::Sequence<sheet::FormulaToken>& rSequence,
    svl::SharedStringPool& rSPool, ExternalReferenceHelper* pExtRef )
{
    bool bError = false;
    const sal_Int32 nCount = rSequence.getLength();
    for (sal_Int32 nPos=0; nPos<nCount; nPos++)
    {
        bool bOneError = AddFormulaToken(rSequence[nPos], rSPool, pExtRef);
        if (bOneError)
        {
            AddOpCode( ocErrName);  // add something that indicates an error
            bError = true;
        }
    }
    return bError;
}
 
void FormulaTokenArray::DelRPN()
{
    if( nRPN )
    {
        FormulaToken** p = pRPN;
        for( sal_uInt16 i = 0; i < nRPN; i++ )
        {
            (*p++)->DecRef();
        }
        delete [] pRPN;
    }
    pRPN = nullptr;
    nRPN = 0;
}
 
FormulaToken* FormulaTokenArray::FirstToken() const
{
    if (!pCode || nLen == 0)
        return nullptr;
    return pCode[0];
}
 
FormulaToken* FormulaTokenArray::PeekPrev( sal_uInt16 & nIdx ) const
{
    if (0 < nIdx && nIdx <= nLen)
        return pCode[--nIdx];
    return nullptr;
}
 
FormulaToken* FormulaTokenArray::FirstRPNToken() const
{
    if (!pRPN || nRPN == 0)
        return nullptr;
    return pRPN[0];
}
 
FormulaToken* FormulaTokenArray::LastRPNToken() const
{
    if (!pRPN || nRPN == 0)
        return nullptr;
    return pRPN[nRPN - 1];
}
 
bool FormulaTokenArray::HasReferences() const
{
    for (auto i: Tokens())
    {
        if (i->IsRef())
            return true;
    }
 
    for (auto i: RPNTokens())
    {
        if (i->IsRef())
            return true;
    }
 
    return false;
}
 
bool FormulaTokenArray::HasExternalRef() const
{
    for (auto i: Tokens())
    {
        if (i->IsExternalRef())
            return true;
    }
    return false;
}
 
bool FormulaTokenArray::HasOpCode( OpCode eOp ) const
{
    for (auto i: Tokens())
    {
        if (i->GetOpCode() == eOp)
            return true;
    }
    return false;
}
 
bool FormulaTokenArray::HasOpCodeRPN( OpCode eOp ) const
{
    for (auto i: RPNTokens())
    {
        if (i->GetOpCode() == eOp)
            return true;
    }
    return false;
}
 
bool FormulaTokenArray::HasNameOrColRowName() const
{
    for (auto i: Tokens())
    {
        if (i->GetType() == svIndex || i->GetOpCode() == ocColRowName )
            return true;
    }
    return false;
}
 
bool FormulaTokenArray::HasOpCodes(const unordered_opcode_set& rOpCodes) const
{
    for (auto i: Tokens())
    {
        if (rOpCodes.count(i->GetOpCode()) > 0)
            return true;
    }
 
    return false;
}
 
FormulaTokenArray::FormulaTokenArray() :
    pRPN(nullptr),
    nLen(0),
    nRPN(0),
    nError(FormulaError::NONE),
    nMode(ScRecalcMode::NORMAL),
    bHyperLink(false),
    mbFromRangeName(false),
    mbShareable(true),
    mbFinalized(false)
{
}
 
FormulaTokenArray::FormulaTokenArray( const FormulaTokenArray& rArr )
{
    Assign( rArr );
}
 
FormulaTokenArray::FormulaTokenArray( FormulaTokenArray&& rArr )
{
    Move( std::move(rArr) );
}
 
FormulaTokenArray::~FormulaTokenArray()
{
    FormulaTokenArray::Clear();
}
 
void FormulaTokenArray::Finalize()
{
    if( nLen && !mbFinalized )
    {
        // Add() overallocates, so reallocate to the minimum needed size.
        std::unique_ptr<FormulaToken*[]> newCode(new FormulaToken*[ nLen ]);
        std::copy(&pCode[0], &pCode[nLen], newCode.get());
        pCode = std::move( newCode );
        mbFinalized = true;
    }
}
 
void FormulaTokenArray::Assign( const FormulaTokenArray& r )
{
    nLen   = r.nLen;
    nRPN   = r.nRPN;
    nError = r.nError;
    nMode  = r.nMode;
    bHyperLink = r.bHyperLink;
    mbFromRangeName = r.mbFromRangeName;
    mbShareable = r.mbShareable;
    mbFinalized = r.mbFinalized;
    pCode  = nullptr;
    pRPN   = nullptr;
    FormulaToken** pp;
    if( nLen )
    {
        pCode.reset(new FormulaToken*[ nLen ]);
        pp = pCode.get();
        memcpy( pp, r.pCode.get(), nLen * sizeof( FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nLen; i++ )
            (*pp++)->IncRef();
        mbFinalized = true;
    }
    if( nRPN )
    {
        pp = pRPN = new FormulaToken*[ nRPN ];
        memcpy( pp, r.pRPN, nRPN * sizeof( FormulaToken* ) );
        for( sal_uInt16 i = 0; i < nRPN; i++ )
            (*pp++)->IncRef();
    }
}
 
void FormulaTokenArray::Move( FormulaTokenArray&& r )
{
    pCode  = std::move(r.pCode);
    pRPN   = r.pRPN;
    r.pRPN = nullptr;
    nLen   = r.nLen;
    r.nLen = 0;
    nRPN   = r.nRPN;
    r.nRPN = 0;
    nError = r.nError;
    nMode  = r.nMode;
    bHyperLink = r.bHyperLink;
    mbFromRangeName = r.mbFromRangeName;
    mbShareable = r.mbShareable;
    mbFinalized = r.mbFinalized;
}
 
/// Optimisation for efficiently creating StringXML placeholders
void FormulaTokenArray::Assign( sal_uInt16 nCode, FormulaToken **pTokens )
{
    assert( nLen == 0 );
    assert( pCode == nullptr );
 
    nLen = nCode;
    pCode.reset(new FormulaToken*[ nLen ]);
    mbFinalized = true;
 
    for( sal_uInt16 i = 0; i < nLen; i++ )
    {
        FormulaToken *t = pTokens[ i ];
        assert( t->GetOpCode() == ocStringXML );
        pCode[ i ] = t;
        t->IncRef();
    }
}
 
FormulaTokenArray& FormulaTokenArray::operator=( const FormulaTokenArray& rArr )
{
    if(this == &rArr)
        return *this;
 
    Clear();
    Assign( rArr );
    return *this;
}
 
FormulaTokenArray& FormulaTokenArray::operator=( FormulaTokenArray&& rArr )
{
    Clear();
    Move( std::move(rArr) );
    return *this;
}
 
void FormulaTokenArray::Clear()
{
    if( nRPN ) DelRPN();
    if( pCode )
    {
        FormulaToken** p = pCode.get();
        for( sal_uInt16 i = 0; i < nLen; i++ )
        {
            (*p++)->DecRef();
        }
        pCode.reset();
    }
    pRPN = nullptr;
    nError = FormulaError::NONE;
    nLen = nRPN = 0;
    bHyperLink = false;
    mbFromRangeName = false;
    mbShareable = true;
    mbFinalized = false;
    ClearRecalcMode();
}
 
void FormulaTokenArray::CheckToken( const FormulaToken& /*r*/ )
{
    // Do nothing.
}
 
void FormulaTokenArray::CheckAllRPNTokens()
{
    if( nRPN )
    {
        FormulaToken** p = pRPN;
        for( sal_uInt16 i = 0; i < nRPN; i++ )
        {
            CheckToken( *p[ i ] );
        }
    }
}
 
FormulaToken* FormulaTokenArray::AddToken( const FormulaToken& r )
{
    return Add( r.Clone() );
}
 
FormulaToken* FormulaTokenArray::MergeArray( )
{
    return nullptr;
}
 
FormulaToken* FormulaTokenArray::ReplaceToken( sal_uInt16 nOffset, FormulaToken* t,
        FormulaTokenArray::ReplaceMode eMode )
{
    if (nOffset < nLen)
    {
        CheckToken(*t);
        t->IncRef();
        FormulaToken* p = pCode[nOffset];
        pCode[nOffset] = t;
        if (eMode == CODE_AND_RPN && p->GetRef() > 1)
        {
            for (sal_uInt16 i=0; i < nRPN; ++i)
            {
                if (pRPN[i] == p)
                {
                    t->IncRef();
                    pRPN[i] = t;
                    p->DecRef();
                    if (p->GetRef() == 1)
                        break;  // for
                }
            }
        }
        p->DecRef();    // may be dead now
        return t;
    }
    else
    {
        t->DeleteIfZeroRef();
        return nullptr;
    }
}
 
sal_uInt16 FormulaTokenArray::RemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
{
    if (nOffset < nLen)
    {
        SAL_WARN_IF( nOffset + nCount > nLen, "formula.core",
                "FormulaTokenArray::RemoveToken - nOffset " << nOffset << " + nCount " << nCount << " > nLen " << nLen);
        const sal_uInt16 nStop = ::std::min( static_cast<sal_uInt16>(nOffset + nCount), nLen);
        nCount = nStop - nOffset;
        for (sal_uInt16 j = nOffset; j < nStop; ++j)
        {
            FormulaToken* p = pCode[j];
            if (p->GetRef() > 1)
            {
                for (sal_uInt16 i=0; i < nRPN; ++i)
                {
                    if (pRPN[i] == p)
                    {
                        // Shift remaining tokens in pRPN down.
                        for (sal_uInt16 x=i+1; x < nRPN; ++x)
                        {
                            pRPN[x-1] = pRPN[x];
                        }
                        --nRPN;
 
                        p->DecRef();
                        if (p->GetRef() == 1)
                            break;  // for
                    }
                }
            }
            p->DecRef();    // may be dead now
        }
 
        // Shift remaining tokens in pCode down.
        for (sal_uInt16 x = nStop; x < nLen; ++x)
        {
            pCode[x-nCount] = pCode[x];
        }
        nLen -= nCount;
 
        return nCount;
    }
    else
    {
        SAL_WARN("formula.core","FormulaTokenArray::RemoveToken - nOffset " << nOffset << " >= nLen " << nLen);
        return 0;
    }
}
 
FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
{
    assert(!mbFinalized);
    if (mbFinalized)
    {
        t->DeleteIfZeroRef();
        return nullptr;
    }
 
// Allocating an array of size FORMULA_MAXTOKENS is simple, but that results in relatively large
// allocations that malloc() implementations usually do not handle as efficiently as smaller
// sizes (not only in terms of memory usage but also speed). Since most token arrays are going
// to be small, start with a small array and resize only if needed. Eventually Finalize() will
// reallocate the memory to size exactly matching the requirements.
    const size_t MAX_FAST_TOKENS = 32;
    if( !pCode )
        pCode.reset(new FormulaToken*[ MAX_FAST_TOKENS ]);
    if( nLen == MAX_FAST_TOKENS )
    {
        FormulaToken** tmp = new FormulaToken*[ FORMULA_MAXTOKENS ];
        std::copy(&pCode[0], &pCode[MAX_FAST_TOKENS], tmp);
        pCode.reset(tmp);
    }
    if( nLen < FORMULA_MAXTOKENS - 1 )
    {
        CheckToken(*t);
        pCode[ nLen++ ] = t;
        t->IncRef();
        if( t->GetOpCode() == ocArrayClose )
            return MergeArray();
        return t;
    }
    else
    {
        t->DeleteIfZeroRef();
        if ( nLen == FORMULA_MAXTOKENS - 1 )
        {
            t = new FormulaByteToken( ocStop );
            pCode[ nLen++ ] = t;
            t->IncRef();
        }
        return nullptr;
    }
}
 
FormulaToken* FormulaTokenArray::AddString( const svl::SharedString& rStr )
{
    return Add( new FormulaStringToken( rStr ) );
}
 
FormulaToken* FormulaTokenArray::AddDouble( double fVal )
{
    return Add( new FormulaDoubleToken( fVal ) );
}
 
void FormulaTokenArray::AddExternal( const sal_Unicode* pStr )
{
    AddExternal( OUString( pStr ) );
}
 
FormulaToken* FormulaTokenArray::AddExternal( const OUString& rStr,
        OpCode eOp /* = ocExternal */ )
{
    return Add( new FormulaExternalToken( eOp, rStr ) );
}
 
FormulaToken* FormulaTokenArray::AddBad( const OUString& rStr )
{
    return Add( new FormulaStringOpToken( ocBad, svl::SharedString( rStr ) ) ); // string not interned
}
 
FormulaToken* FormulaTokenArray::AddStringXML( const OUString& rStr )
{
    return Add( new FormulaStringOpToken( ocStringXML, svl::SharedString( rStr ) ) );   // string not interned
}
 
FormulaToken* FormulaTokenArray::AddStringName( const OUString& rStr )
{
    return Add( new FormulaStringOpToken( ocStringName, svl::SharedString( rStr ) ) );   // string not interned
}
 
void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits )
{
    const unsigned nExclusive = static_cast<sal_uInt8>(nBits & ScRecalcMode::EMask);
    if (nExclusive)
    {
        unsigned nExBit;
        if (nExclusive & (nExclusive - 1))
        {
            // More than one bit set, use highest priority.
            for (nExBit = 1; (nExBit & static_cast<sal_uInt8>(ScRecalcMode::EMask)) != 0; nExBit <<= 1)
            {
                if (nExclusive & nExBit)
                    break;
            }
        }
        else
        {
            // Only one bit is set.
            nExBit = nExclusive;
        }
        // Set exclusive bit if priority is higher than existing.
        if (nExBit < static_cast<sal_uInt8>(nMode & ScRecalcMode::EMask))
            SetMaskedRecalcMode( static_cast<ScRecalcMode>(nExBit));
    }
    SetCombinedBitsRecalcMode( nBits );
}
 
 
bool FormulaTokenArray::HasMatrixDoubleRefOps() const
{
    if ( !pRPN || !nRPN )
        return false;
 
    // RPN-Interpreter simulation.
    // Simply assumes a double as return value of each function.
    std::unique_ptr<FormulaToken*[]> pStack(new FormulaToken* [nRPN]);
    FormulaToken* pResult = new FormulaDoubleToken( 0.0 );
    short sp = 0;
    for ( auto t: RPNTokens() )
    {
        OpCode eOp = t->GetOpCode();
        sal_uInt8 nParams = t->GetParamCount();
        switch ( eOp )
        {
            case ocAdd :
            case ocSub :
            case ocMul :
            case ocDiv :
            case ocPow :
            case ocPower :
            case ocAmpersand :
            case ocEqual :
            case ocNotEqual :
            case ocLess :
            case ocGreater :
            case ocLessEqual :
            case ocGreaterEqual :
            {
                for ( sal_uInt8 k = nParams; k; k-- )
                {
                    if ( sp >= k && pStack[sp-k]->GetType() == svDoubleRef )
                    {
                        pResult->Delete();
                        return true;
                    }
                }
            }
            break;
            default:
            {
                // added to avoid warnings
            }
        }
        if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() )  )
            pStack[sp++] = t;
        else if (FormulaCompiler::IsOpCodeJumpCommand( eOp ))
        {   // ignore Jumps, pop previous Result (Condition)
            if ( sp )
                --sp;
        }
        else
        {   // pop parameters, push result
            sp = sal::static_int_cast<short>( sp - nParams );
            if ( sp < 0 )
            {
                SAL_WARN("formula.core", "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" );
                sp = 0;
            }
            pStack[sp++] = pResult;
        }
    }
    pResult->Delete();
 
    return false;
}
 
// --- Formula rewrite of a token array
 
inline bool MissingConventionODF::isRewriteNeeded( OpCode eOp ) const
{
    switch (eOp)
    {
        case ocGammaDist:
        case ocPoissonDist:
        case ocAddress:
        case ocLogInv:
        case ocLogNormDist:
        case ocNormDist:
            return true;
        case ocMissing:
        case ocLog:
            return isPODF();    // rewrite only for PODF
        case ocDBCount:
        case ocDBCount2:
            return isODFF();    // rewrite only for ODFF
        default:
            return false;
    }
}
 
/*
 fdo 81596
To be implemented yet:
  ocExternal:    ?
  ocMacro:       ?
  ocIndex:       INDEX() ?
*/
inline bool MissingConventionOOXML::isRewriteNeeded( OpCode eOp )
{
    switch (eOp)
    {
        case ocIf:
 
        case ocExternal:
        case ocEuroConvert:
        case ocMacro:
 
        case ocRound:
        case ocRoundUp:
        case ocRoundDown:
 
        case ocIndex:
 
        case ocCeil:
        case ocFloor:
 
        case ocGammaDist:
        case ocFDist_LT:
        case ocPoissonDist:
        case ocNormDist:
        case ocLogInv:
        case ocLogNormDist:
        case ocHypGeomDist:
 
        case ocDBCount:
        case ocDBCount2:
            return true;
 
        default:
            return false;
    }
}
 
namespace {
 
class FormulaMissingContext
{
    public:
            const FormulaToken* mpFunc;
            int                 mnCurArg;
 
                    void    Clear() { mpFunc = nullptr; mnCurArg = 0; }
            inline  bool    AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const;
                    bool    AddMissingExternal( FormulaTokenArray* pNewArr ) const;
                    bool    AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const;
                    void    AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const;
};
 
}
 
void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const
{
    if ( !mpFunc )
        return;
 
    switch (rConv.getConvention())
    {
        case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
        case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
            {
                switch (mpFunc->GetOpCode())
                {
                    case ocGammaDist:
                        if (mnCurArg == 2)
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
                        }
                        break;
                    case ocPoissonDist:
                        if (mnCurArg == 1)
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 3rd, Cumulative=true()
                        }
                        break;
                    case ocNormDist:
                        if ( mnCurArg == 2 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
                        }
                        break;
                    case ocLogInv:
                    case ocLogNormDist:
                        if ( mnCurArg == 0 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 0.0 );      // 2nd, mean = 0.0
                        }
                        if ( mnCurArg <= 1 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 3rd, standard deviation = 1.0
                        }
                        break;
                    case ocLog:
                        if ( rConv.isPODF() && mnCurArg == 0 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 10.0 );     // 2nd, basis 10
                        }
                        break;
                    default:
                        break;
                }
            }
            break;
        case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
            {
                switch (mpFunc->GetOpCode())
                {
                    case ocIf:
                        if( mnCurArg == 0 )
                        {
                            // Excel needs at least two parameters in IF function
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddOpCode( ocTrue );   // 2nd, true() as function
                            pNewArr->AddOpCode( ocOpen );   // so the result is of logical type
                            pNewArr->AddOpCode( ocClose );  // and survives roundtrip
                        }
                        break;
 
                    case ocEuroConvert:
                        if ( mnCurArg == 2 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 0.0 );      // 4th, FullPrecision = false()
                        }
                        break;
 
                    case ocPoissonDist:
                        if (mnCurArg == 1)
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 3rd, Cumulative=true()
                        }
                        break;
 
                    case ocGammaDist:
                    case ocFDist_LT:
                    case ocNormDist:
                        if (mnCurArg == 2)
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 4th, Cumulative=true()
                        }
                        break;
 
                    case ocLogInv:
                    case ocLogNormDist:
                        if ( mnCurArg == 0 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 0.0 );      // 2nd, mean = 0.0
                        }
                        if ( mnCurArg <= 1 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 1.0 );      // 3rd, standard deviation = 1.0
                        }
                        break;
 
                    case ocHypGeomDist:
                        if ( mnCurArg == 3 )
                        {
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 0.0 );      // 5th, Cumulative = false()
                        }
                        break;
 
                    case ocRound:
                    case ocRoundUp:
                    case ocRoundDown:
                        if( mnCurArg == 0 )
                        {
                            // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
                            pNewArr->AddOpCode( ocSep );
                            pNewArr->AddDouble( 0.0 );      // 2nd, 0.0
                        }
                        break;
 
                    default:
                        break;
                }
            }
            break;
    }
 
}
 
inline bool FormulaMissingContext::AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const
{
    if (mnCurArg == nArg)
    {
        pNewArr->AddDouble( f );
        return true;
    }
    return false;
}
 
bool FormulaMissingContext::AddMissingExternal( FormulaTokenArray *pNewArr ) const
{
    // Only called for PODF, not ODFF. No need to distinguish.
 
    const OUString &rName = mpFunc->GetExternal();
 
    // initial (fast) checks:
    sal_Int32 nLength = rName.getLength();
    if (!nLength)
        return false;
 
    sal_Unicode nLastChar = rName[ nLength - 1];
    if ( nLastChar != 't' && nLastChar != 'm' )
        return false;
 
    if (rName.equalsIgnoreAsciiCase(
                "com.sun.star.sheet.addin.Analysis.getAccrint" ))
    {
        return AddDefaultArg( pNewArr, 4, 1000.0 );
    }
    if (rName.equalsIgnoreAsciiCase(
                "com.sun.star.sheet.addin.Analysis.getAccrintm" ))
    {
        return AddDefaultArg( pNewArr, 3, 1000.0 );
    }
    return false;
}
 
bool FormulaMissingContext::AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv  ) const
{
    if ( !mpFunc )
        return false;
 
    bool bRet = false;
    const OpCode eOp = mpFunc->GetOpCode();
 
    switch (rConv.getConvention())
    {
        case MissingConvention::FORMULA_MISSING_CONVENTION_ODFF:
            {
                // Add for ODFF
                switch (eOp)
                {
                    case ocAddress:
                        return AddDefaultArg( pNewArr, 2, 1.0 );    // abs
                    default:
                        break;
                }
            }
            break;
        case MissingConvention::FORMULA_MISSING_CONVENTION_PODF:
            {
                // Add for PODF
                switch (eOp)
                {
                    case ocAddress:
                        return AddDefaultArg( pNewArr, 2, 1.0 );    // abs
                    case ocFixed:
                        return AddDefaultArg( pNewArr, 1, 2.0 );
                    case ocBetaDist:
                    case ocBetaInv:
                    case ocPMT:
                        return AddDefaultArg( pNewArr, 3, 0.0 );
                    case ocIpmt:
                    case ocPpmt:
                        return AddDefaultArg( pNewArr, 4, 0.0 );
                    case ocPV:
                    case ocFV:
                        bRet |= AddDefaultArg( pNewArr, 2, 0.0 );   // pmt
                        bRet |= AddDefaultArg( pNewArr, 3, 0.0 );   // [fp]v
                        break;
                    case ocRate:
                        bRet |= AddDefaultArg( pNewArr, 1, 0.0 );   // pmt
                        bRet |= AddDefaultArg( pNewArr, 3, 0.0 );   // fv
                        bRet |= AddDefaultArg( pNewArr, 4, 0.0 );   // type
                        break;
                    case ocExternal:
                        return AddMissingExternal( pNewArr );
 
                        // --- more complex cases ---
 
                    case ocOffset:
                        // FIXME: rather tough
                        // if arg 3 (height) omitted, export arg1 (rows)
                        break;
                    default:
                        break;
                }
            }
            break;
        case MissingConvention::FORMULA_MISSING_CONVENTION_OOXML:
            {
                switch (eOp)
                {
                    case ocExternal:
                        return AddMissingExternal( pNewArr );
                    default:
                        break;
                }
            }
            break;
    }
 
    return bRet;
}
 
bool FormulaTokenArray::NeedsPodfRewrite( const MissingConventionODF & rConv )
{
    for ( auto i: Tokens()  )
    {
        if ( rConv.isRewriteNeeded( i->GetOpCode()))
            return true;
    }
    return false;
}
 
bool FormulaTokenArray::NeedsOoxmlRewrite()
{
    for ( auto i: Tokens() )
    {
        if ( MissingConventionOOXML::isRewriteNeeded( i->GetOpCode()))
            return true;
    }
    return false;
}
 
 
FormulaTokenArray * FormulaTokenArray::RewriteMissing( const MissingConvention & rConv )
{
    const size_t nAlloc = 256;
    FormulaMissingContext aCtx[ nAlloc ];
 
    /* TODO: with some effort we might be able to merge the two almost
     * identical function stacks into one and generalize things, otherwise
     * adding yet another "omit argument" would be copypasta. */
 
    int aOpCodeAddressStack[ nAlloc ];  // use of ADDRESS() function
    const int nOmitAddressArg = 3;      // ADDRESS() 4th parameter A1/R1C1
 
    int aOpCodeDcountStack[ nAlloc ];   // use of DCOUNT()/DCOUNTA() function
    const int nOmitDcountArg = 1;       // DCOUNT() and DCOUNTA() 2nd parameter DatabaseField if 0
 
    sal_uInt16 nTokens = GetLen() + 1;
    FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]);
    int* pOcas = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeAddressStack[0]);
    int* pOcds = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeDcountStack[0]);
    // Never go below 0, never use 0, mpFunc always NULL.
    pCtx[0].Clear();
    int nFn = 0;
    int nOcas = 0;
    int nOcds = 0;
 
    FormulaTokenArray *pNewArr = new FormulaTokenArray;
    // At least ScRecalcMode::ALWAYS needs to be set.
    pNewArr->AddRecalcMode( GetRecalcMode());
 
    FormulaTokenArrayPlainIterator aIter(*this);
    for ( FormulaToken *pCur = aIter.First(); pCur; pCur = aIter.Next() )
    {
        bool bAdd = true;
        // Don't write the expression of the new inserted ADDRESS() parameter.
        // Do NOT omit the new second parameter of INDIRECT() though. If that
        // was done for both, INDIRECT() actually could calculate different and
        // valid (but wrong) results with the then changed return value of
        // ADDRESS(). Better let it generate an error instead.
        for (int i = nOcas; i-- > 0 && bAdd; )
        {
            if (pCtx[ pOcas[ i ] ].mnCurArg == nOmitAddressArg)
            {
                // Omit everything except a trailing separator, the leading
                // separator is omitted below. The other way around would leave
                // an extraneous separator if no parameter followed.
                if (pOcas[ i ] != nFn || pCur->GetOpCode() != ocSep)
                    bAdd = false;
            }
        }
        // Strip the 2nd argument (leaving empty) of DCOUNT() and DCOUNTA() if
        // it is 0.
        for (int i = nOcds; i-- > 0 && bAdd; )
        {
            if (pCtx[ pOcds[ i ] ].mnCurArg == nOmitDcountArg)
            {
                // Omit only a literal 0 value, nothing else.
                if (pOcds[ i ] == nFn && pCur->GetOpCode() == ocPush && pCur->GetType() == svDouble &&
                        pCur->GetDouble() == 0.0)
                {
                    // No other expression, between separators.
                    FormulaToken* p = aIter.PeekPrevNoSpaces();
                    if (p && p->GetOpCode() == ocSep)
                    {
                        p = aIter.PeekNextNoSpaces();
                        if (p && p->GetOpCode() == ocSep)
                            bAdd = false;
                    }
                }
            }
        }
        switch ( pCur->GetOpCode() )
        {
            case ocOpen:
                {
                    ++nFn;      // all following operations on _that_ function
                    pCtx[ nFn ].mpFunc = aIter.PeekPrevNoSpaces();
                    pCtx[ nFn ].mnCurArg = 0;
                    if (rConv.isPODF() && pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress)
                        pOcas[ nOcas++ ] = nFn;     // entering ADDRESS() if PODF
                    else if ((rConv.isODFF() || rConv.isOOXML()) && pCtx[ nFn ].mpFunc)
                    {
                        OpCode eOp = pCtx[ nFn ].mpFunc->GetOpCode();
                        if (eOp == ocDBCount || eOp == ocDBCount2)
                            pOcds[ nOcds++ ] = nFn;     // entering DCOUNT() or DCOUNTA() if ODFF or OOXML
                    }
                }
            break;
            case ocClose:
                pCtx[ nFn ].AddMoreArgs( pNewArr, rConv );
                SAL_WARN_IF(nFn <= 0, "formula.core", "FormulaTokenArray::RewriteMissing: underflow");
                if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn)
                    --nOcas;                    // leaving ADDRESS()
                else if (nOcds > 0 && pOcds[ nOcds-1 ] == nFn)
                    --nOcds;                    // leaving DCOUNT() or DCOUNTA()
                if (nFn > 0)
                    --nFn;
                break;
            case ocSep:
                pCtx[ nFn ].mnCurArg++;
                // Omit leading separator of ADDRESS() parameter.
                if (nOcas && pOcas[ nOcas-1 ] == nFn && pCtx[ nFn ].mnCurArg == nOmitAddressArg)
                {
                    bAdd = false;
                }
                break;
            case ocMissing:
                if ( bAdd )
                    bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv );
                break;
            default:
                break;
        }
        if (bAdd)
        {
            OpCode eOp = pCur->GetOpCode();
            if ( ( eOp == ocCeil || eOp == ocFloor ||
                   ( eOp == ocLogNormDist && pCur->GetByte() == 4 ) ) &&
                 rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
            {
                switch ( eOp )
                {
                    case ocCeil :
                        eOp = ocCeil_Math;
                        break;
                    case ocFloor :
                        eOp = ocFloor_Math;
                        break;
                    case ocLogNormDist :
                        eOp = ocLogNormDist_MS;
                        break;
                    default :
                        eOp = ocNone;
                        break;
                }
                FormulaToken *pToken = new FormulaToken( svByte, eOp );
                pNewArr->Add( pToken );
            }
            else if ( eOp == ocHypGeomDist &&
                      rConv.getConvention() == MissingConvention::FORMULA_MISSING_CONVENTION_OOXML )
            {
                FormulaToken *pToken = new FormulaToken( svByte, ocHypGeomDist_MS );
                pNewArr->Add( pToken );
            }
            else
                pNewArr->AddToken( *pCur );
        }
    }
 
    if (pOcds != &aOpCodeDcountStack[0])
        delete [] pOcds;
    if (pOcas != &aOpCodeAddressStack[0])
        delete [] pOcas;
    if (pCtx != &aCtx[0])
        delete [] pCtx;
 
    return pNewArr;
}
 
namespace {
inline bool isWhitespace( OpCode eOp ) { return eOp == ocSpaces || eOp == ocWhitespace; }
}
 
bool FormulaTokenArray::MayReferenceFollow()
{
    if ( !pCode || nLen <= 0 )
        return false;
 
    // ignore trailing spaces
    sal_uInt16 i = nLen - 1;
    while (i > 0 && isWhitespace( pCode[i]->GetOpCode()))
    {
        --i;
    }
    if (i > 0 || !isWhitespace( pCode[i]->GetOpCode()))
    {
        OpCode eOp = pCode[i]->GetOpCode();
        if ( (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) ||
             (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP ) ||
             eOp == SC_OPCODE_OPEN || eOp == SC_OPCODE_SEP )
        {
            return true;
        }
    }
    return false;
}
FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp )
{
    FormulaToken* pRet = nullptr;
    switch ( eOp )
    {
        case ocOpen:
        case ocClose:
        case ocSep:
        case ocArrayOpen:
        case ocArrayClose:
        case ocArrayRowSep:
        case ocArrayColSep:
            pRet = new FormulaToken( svSep,eOp );
            break;
        case ocIf:
        case ocIfError:
        case ocIfNA:
        case ocChoose:
        case ocLet:
            {
                short nJump[FORMULA_MAXPARAMS + 1];
                if ( eOp == ocIf )
                    nJump[ 0 ] = 3;
                else if ( eOp == ocChoose )
                    nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
                else if ( eOp == ocLet )
                    nJump[ 0 ] = FORMULA_MAXPARAMS + 1;
                else
                    nJump[ 0 ] = 2;
                pRet = new FormulaJumpToken( eOp, nJump );
            }
            break;
        default:
            pRet = new FormulaByteToken( eOp, 0, ParamClass::Unknown );
            break;
    }
    return Add( pRet );
}
 
void FormulaTokenArray::ReinternStrings( svl::SharedStringPool& rPool )
{
    for (auto i: Tokens())
    {
        switch (i->GetType())
        {
            case svString:
                i->SetString( rPool.intern( i->GetString().getString()));
                break;
            default:
                ;   // nothing
        }
    }
}
 
 
/*----------------------------------------------------------------------*/
 
FormulaTokenIterator::Item::Item(const FormulaTokenArray* pArray, short pc, short stop) :
    pArr(pArray), nPC(pc), nStop(stop)
{
}
 
FormulaTokenIterator::FormulaTokenIterator( const FormulaTokenArray& rArr )
    : maStack{ FormulaTokenIterator::Item(&rArr, -1, SHRT_MAX) }
{
}
 
FormulaTokenIterator::~FormulaTokenIterator()
{
}
 
void FormulaTokenIterator::Push( const FormulaTokenArray* pArr )
{
    FormulaTokenIterator::Item item(pArr, -1, SHRT_MAX);
 
    maStack.push_back(item);
}
 
void FormulaTokenIterator::Pop()
{
    maStack.pop_back();
}
 
void FormulaTokenIterator::Reset()
{
    while( maStack.size() > 1 )
        maStack.pop_back();
 
    maStack.back().nPC = -1;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextName()
{
    if( mpFTA->GetArray() )
    {
        while ( mnIndex < mpFTA->GetLen() )
        {
            FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
            if( t->GetType() == svIndex )
                return t;
        }
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextStringName()
{
    if (mpFTA->GetArray())
    {
        while (mnIndex < mpFTA->GetLen())
        {
            FormulaToken* t = mpFTA->GetArray()[mnIndex++];
            if (t->GetType() == svString && t->GetOpCode() == ocStringName)
                return t;
        }
    }
    return nullptr;
}
 
const FormulaToken* FormulaTokenIterator::Next()
{
    const FormulaToken* t = GetNonEndOfPathToken( ++maStack.back().nPC );
    if( !t && maStack.size() > 1 )
    {
        Pop();
        t = Next();
    }
    return t;
}
 
const FormulaToken* FormulaTokenIterator::PeekNextOperator()
{
    const FormulaToken* t = nullptr;
    short nIdx = maStack.back().nPC;
    for (;;)
    {
        t = GetNonEndOfPathToken( ++nIdx);
        if (t == nullptr || t->GetOpCode() != ocPush)
            break;   // ignore operands
    }
    if (!t && maStack.size() > 1)
    {
        FormulaTokenIterator::Item aHere = maStack.back();
        maStack.pop_back();
        t = PeekNextOperator();
        maStack.push_back(aHere);
    }
    return t;
}
 
//! The nPC counts after a Push() are -1
 
void FormulaTokenIterator::Jump( short nStart, short nNext, short nStop )
{
    maStack.back().nPC = nNext;
    if( nStart != nNext )
    {
        Push( maStack.back().pArr );
        maStack.back().nPC = nStart;
        maStack.back().nStop = nStop;
    }
}
 
void FormulaTokenIterator::ReInit( const FormulaTokenArray& rArr )
{
    maStack.clear();
    Push( &rArr );
}
 
const FormulaToken* FormulaTokenIterator::GetNonEndOfPathToken( short nIdx ) const
{
    FormulaTokenIterator::Item cur = maStack.back();
 
    if (nIdx < cur.pArr->GetCodeLen() && nIdx < cur.nStop)
    {
        const FormulaToken* t = cur.pArr->GetCode()[ nIdx ];
        // such an OpCode ends an IF() or CHOOSE() path
        return (t->GetOpCode() == ocSep || t->GetOpCode() == ocClose) ? nullptr : t;
    }
    return nullptr;
}
 
bool FormulaTokenIterator::IsEndOfPath() const
{
    return GetNonEndOfPathToken( maStack.back().nPC + 1) == nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextReference()
{
    while( mnIndex < mpFTA->GetLen() )
    {
        FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
        switch( t->GetType() )
        {
            case svSingleRef:
            case svDoubleRef:
            case svExternalSingleRef:
            case svExternalDoubleRef:
                return t;
            default:
            {
                // added to avoid warnings
            }
        }
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextColRowName()
{
    while( mnIndex < mpFTA->GetLen() )
    {
        FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
        if ( t->GetOpCode() == ocColRowName )
            return t;
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceRPN()
{
    while( mnIndex < mpFTA->GetCodeLen() )
    {
        FormulaToken* t = mpFTA->GetCode()[ mnIndex++ ];
        switch( t->GetType() )
        {
            case svSingleRef:
            case svDoubleRef:
            case svExternalSingleRef:
            case svExternalDoubleRef:
                return t;
            default:
            {
                // added to avoid warnings
            }
        }
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::GetNextReferenceOrName()
{
    if( !mpFTA->GetArray() )
        return nullptr;
 
    while ( mnIndex < mpFTA->GetLen() )
    {
        FormulaToken* t = mpFTA->GetArray()[ mnIndex++ ];
        switch( t->GetType() )
        {
            case svSingleRef:
            case svDoubleRef:
            case svIndex:
            case svExternalSingleRef:
            case svExternalDoubleRef:
            case svExternalName:
                return t;
            default:
            {
                // added to avoid warnings
            }
         }
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::Next()
{
    if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
        return mpFTA->GetArray()[ mnIndex++ ];
    else
        return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::NextNoSpaces()
{
    if( mpFTA->GetArray() )
    {
        while ((mnIndex < mpFTA->GetLen()) && isWhitespace( mpFTA->GetArray()[ mnIndex ]->GetOpCode()))
            ++mnIndex;
        if( mnIndex < mpFTA->GetLen() )
            return mpFTA->GetArray()[ mnIndex++ ];
    }
    return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::NextRPN()
{
    if( mpFTA->GetCode() && mnIndex < mpFTA->GetCodeLen() )
        return mpFTA->GetCode()[ mnIndex++ ];
    else
        return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::PrevRPN()
{
    if( mpFTA->GetCode() && mnIndex )
        return mpFTA->GetCode()[ --mnIndex ];
    else
        return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::PeekNext()
{
    if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
        return mpFTA->GetArray()[ mnIndex ];
    else
        return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::PeekNextNoSpaces() const
{
    if( mpFTA->GetArray() && mnIndex < mpFTA->GetLen() )
    {
        sal_uInt16 j = mnIndex;
        while (j < mpFTA->GetLen() && isWhitespace( mpFTA->GetArray()[j]->GetOpCode()))
            j++;
        if ( j < mpFTA->GetLen() )
            return mpFTA->GetArray()[ j ];
        else
            return nullptr;
    }
    else
        return nullptr;
}
 
FormulaToken* FormulaTokenArrayPlainIterator::PeekPrevNoSpaces() const
{
    if( mpFTA->GetArray() && mnIndex > 1 )
    {
        sal_uInt16 j = mnIndex - 2;
        while (isWhitespace( mpFTA->GetArray()[j]->GetOpCode()) && j > 0 )
            j--;
        if (j > 0 || !isWhitespace( mpFTA->GetArray()[j]->GetOpCode()))
            return mpFTA->GetArray()[ j ];
        else
            return nullptr;
    }
    else
        return nullptr;
}
 
void FormulaTokenArrayPlainIterator::AfterRemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
{
    const sal_uInt16 nStop = std::min( static_cast<sal_uInt16>(nOffset + nCount), mpFTA->GetLen());
 
    if (mnIndex >= nOffset)
    {
        if (mnIndex < nStop)
            mnIndex = nOffset + 1;
        else
            mnIndex -= nStop - nOffset;
    }
}
 
// real implementations of virtual functions
 
sal_Int16 FormulaDoubleToken::GetDoubleType() const
{
    // This is a plain double value without type information, don't emit a
    // warning via FormulaToken::GetDoubleType().
    return 0;
}
 
bool FormulaDoubleToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && fDouble == r.GetDouble();
}
 
sal_Int16 FormulaTypedDoubleToken::GetDoubleType() const
{
    return mnType;
}
 
void FormulaTypedDoubleToken::SetDoubleType( sal_Int16 nType )
{
    mnType = nType;
}
 
bool FormulaTypedDoubleToken::operator==( const FormulaToken& r ) const
{
    return FormulaDoubleToken::operator==( r ) && mnType == r.GetDoubleType();
}
 
FormulaStringToken::FormulaStringToken( svl::SharedString r ) :
    FormulaToken( svString ), maString(std::move( r ))
{
}
 
FormulaStringToken::FormulaStringToken( const FormulaStringToken& r ) :
    FormulaToken( r ), maString( r.maString ) {}
 
FormulaToken* FormulaStringToken::Clone() const
{
    return new FormulaStringToken(*this);
}
 
const svl::SharedString & FormulaStringToken::GetString() const
{
    return maString;
}
 
void FormulaStringToken::SetString( const svl::SharedString& rStr )
{
    maString = rStr;
}
 
bool FormulaStringToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && maString == r.GetString();
}
 
FormulaStringOpToken::FormulaStringOpToken( OpCode e, svl::SharedString r ) :
    FormulaByteToken( e, 0, svString, ParamClass::Unknown ), maString(std::move( r )) {}
 
FormulaStringOpToken::FormulaStringOpToken( const FormulaStringOpToken& r ) :
    FormulaByteToken( r ), maString( r.maString ) {}
 
FormulaToken* FormulaStringOpToken::Clone() const
{
    return new FormulaStringOpToken(*this);
}
 
const svl::SharedString & FormulaStringOpToken::GetString() const
{
    return maString;
}
 
void FormulaStringOpToken::SetString( const svl::SharedString& rStr )
{
    maString = rStr;
}
 
bool FormulaStringOpToken::operator==( const FormulaToken& r ) const
{
    return FormulaByteToken::operator==( r ) && maString == r.GetString();
}
 
sal_uInt16  FormulaIndexToken::GetIndex() const             { return nIndex; }
void        FormulaIndexToken::SetIndex( sal_uInt16 n )     { nIndex = n; }
sal_Int16   FormulaIndexToken::GetSheet() const             { return mnSheet; }
void        FormulaIndexToken::SetSheet( sal_Int16 n )      { mnSheet = n; }
bool FormulaIndexToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) && nIndex == r.GetIndex() &&
        mnSheet == r.GetSheet();
}
const OUString& FormulaExternalToken::GetExternal() const       { return aExternal; }
bool FormulaExternalToken::operator==( const FormulaToken& r ) const
{
    return FormulaByteToken::operator==( r ) && aExternal == r.GetExternal();
}
 
 
FormulaError      FormulaErrorToken::GetError() const             { return nError; }
void            FormulaErrorToken::SetError( FormulaError nErr )  { nError = nErr; }
bool FormulaErrorToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r ) &&
        nError == static_cast< const FormulaErrorToken & >(r).GetError();
}
double          FormulaMissingToken::GetDouble() const       { return 0.0; }
 
const svl::SharedString & FormulaMissingToken::GetString() const
{
    return svl::SharedString::getEmptyString();
}
 
bool FormulaMissingToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r );
}
 
 
bool FormulaUnknownToken::operator==( const FormulaToken& r ) const
{
    return FormulaToken::operator==( r );
}
 
 
} // formula
 
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V773 The function was exited without releasing the 'pCtx' pointer. A memory leak is possible.

V773 The function was exited without releasing the 'pOcas' pointer. A memory leak is possible.

V773 The function was exited without releasing the 'pOcds' pointer. A memory leak is possible.

V781 The value of the 'j' index is checked after it was used. Perhaps there is a mistake in program logic.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V547 Expression '!"virtual dummy called"' is always false.

V1037 Two or more case-branches perform the same actions. Check lines: 1115, 1129