/* -*- 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