blob: f258bba398a42321a5d58c125c2c0b64fd585477 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "xeformula.hxx"
#include <list>
#include <map>
#include <memory>
#include "addincol.hxx"
#include "compiler.hxx"
#include "document.hxx"
#include "externalrefmgr.hxx"
#include "rangelst.hxx"
#include "token.hxx"
#include "tokenarray.hxx"
#include "xehelper.hxx"
#include "xelink.hxx"
#include "xename.hxx"
#include "xestream.hxx"
using namespace ::formula;
// External reference log =====================================================
XclExpRefLogEntry::XclExpRefLogEntry() :
mpUrl( 0 ),
mpFirstTab( 0 ),
mpLastTab( 0 ),
mnFirstXclTab( EXC_TAB_DELETED ),
mnLastXclTab( EXC_TAB_DELETED )
{
}
// Formula compiler ===========================================================
namespace {
/** Wrapper structure for a processed Calc formula token with additional
settings (whitespaces). */
struct XclExpScToken
{
const FormulaToken* mpScToken; /// Currently processed Calc token.
sal_uInt8 mnSpaces; /// Number of spaces before the Calc token.
inline explicit XclExpScToken() : mpScToken( 0 ), mnSpaces( 0 ) {}
inline bool Is() const { return mpScToken != 0; }
inline StackVar GetType() const { return mpScToken ? mpScToken->GetType() : static_cast< StackVar >( svUnknown ); }
inline OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : static_cast< OpCode >( ocNone ); }
};
// ----------------------------------------------------------------------------
/** Effective token class conversion types. */
enum XclExpClassConv
{
EXC_CLASSCONV_ORG, /// Keep original class of the token.
EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains uncahnged).
EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains uncahnged).
};
// ----------------------------------------------------------------------------
/** Token class conversion and position of a token in the token array. */
struct XclExpTokenConvInfo
{
sal_uInt16 mnTokPos; /// Position of the token in the token array.
XclFuncParamConv meConv; /// Token class conversion type.
bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
};
/** Vector of token position and conversion for all operands of an operator,
or for all parameters of a function. */
struct XclExpOperandList : public ::std::vector< XclExpTokenConvInfo >
{
inline explicit XclExpOperandList() { reserve( 2 ); }
void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType );
};
void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType )
{
resize( size() + 1 );
XclExpTokenConvInfo& rConvInfo = back();
rConvInfo.mnTokPos = nTokPos;
rConvInfo.meConv = eConv;
rConvInfo.mbValType = bValType;
}
typedef ScfRef< XclExpOperandList > XclExpOperandListRef;
typedef ::std::vector< XclExpOperandListRef > XclExpOperandListVector;
// ----------------------------------------------------------------------------
/** Encapsulates all data needed for a call to an external function (macro, add-in). */
struct XclExpExtFuncData
{
String maFuncName; /// Name of the function.
bool mbVBasic; /// True = Visual Basic macro call.
bool mbHidden; /// True = Create hidden defined name.
inline explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {}
void Set( const String& rFuncName, bool bVBasic, bool bHidden );
};
void XclExpExtFuncData::Set( const String& rFuncName, bool bVBasic, bool bHidden )
{
maFuncName = rFuncName;
mbVBasic = bVBasic;
mbHidden = bHidden;
}
// ----------------------------------------------------------------------------
/** Encapsulates all data needed to process an entire function. */
class XclExpFuncData
{
public:
explicit XclExpFuncData(
const XclExpScToken& rTokData,
const XclFunctionInfo& rFuncInfo,
const XclExpExtFuncData& rExtFuncData );
inline const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; }
inline OpCode GetOpCode() const { return mrFuncInfo.meOpCode; }
inline sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; }
inline bool IsVolatile() const { return mrFuncInfo.IsVolatile(); }
inline bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); }
inline bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); }
inline sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; }
inline const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; }
inline sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; }
const XclFuncParamInfo& GetParamInfo() const;
bool IsCalcOnlyParam() const;
bool IsExcelOnlyParam() const;
void IncParamInfoIdx();
inline sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; }
inline sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; }
inline sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); }
void FinishParam( sal_uInt16 nTokPos );
inline XclExpOperandListRef GetOperandList() const { return mxOperands; }
inline ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; }
inline void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); }
private:
ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens.
const XclExpScToken& mrTokData; /// Data about processed function name token.
const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function.
XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in).
XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters.
const XclFuncParamInfo* mpParamInfo; /// Information for current parameter.
};
XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData,
const XclFunctionInfo& rFuncInfo, const XclExpExtFuncData& rExtFuncData ) :
mrTokData( rTokData ),
mrFuncInfo( rFuncInfo ),
maExtFuncData( rExtFuncData ),
mxOperands( new XclExpOperandList ),
mpParamInfo( rFuncInfo.mpParamInfos )
{
DBG_ASSERT( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" );
// set name of an add-in function
if( (maExtFuncData.maFuncName.Len() == 0) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) )
maExtFuncData.Set( GetScToken().GetExternal(), true, false );
}
const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const
{
static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false };
return mpParamInfo ? *mpParamInfo : saInvalidInfo;
}
bool XclExpFuncData::IsCalcOnlyParam() const
{
return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY);
}
bool XclExpFuncData::IsExcelOnlyParam() const
{
return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY);
}
void XclExpFuncData::IncParamInfoIdx()
{
if( mpParamInfo )
{
// move pointer to next entry, if something explicit follows
if( (static_cast< size_t >( mpParamInfo - mrFuncInfo.mpParamInfos + 1 ) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) )
++mpParamInfo;
// if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
else if( IsExcelOnlyParam() || IsCalcOnlyParam() )
mpParamInfo = 0;
// points to last info, but parameter pairs expected, move to previous info
else if( mrFuncInfo.IsParamPairs() )
--mpParamInfo;
// otherwise: repeat last parameter class
}
}
void XclExpFuncData::FinishParam( sal_uInt16 nTokPos )
{
// write token class conversion info for this parameter
const XclFuncParamInfo& rParamInfo = GetParamInfo();
mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType );
// move to next parameter info structure
IncParamInfoIdx();
}
// compiler configuration -----------------------------------------------------
/** Type of token class handling. */
enum XclExpFmlaClassType
{
EXC_CLASSTYPE_CELL, /// Cell formula, shared formula.
EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation.
EXC_CLASSTYPE_NAME /// Defined name, range list.
};
/** Configuration data of the formula compiler. */
struct XclExpCompConfig
{
XclFormulaType meType; /// Type of the formula to be created.
XclExpFmlaClassType meClassType; /// Token class handling type.
bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global.
bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared).
bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names).
bool mbAllowArrays; /// True = Allow inline arrays.
};
/** The table containing configuration data for all formula types. */
static const XclExpCompConfig spConfigTable[] =
{
// formula type token class type lclLM inCell 3dOnly allowArray
{ EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true },
{ EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true },
{ EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true },
{ EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false },
{ EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false },
{ EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true },
{ EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true },
{ EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false },
{ EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false },
{ EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false }
};
// ----------------------------------------------------------------------------
/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */
struct XclExpCompData
{
typedef ScfRef< ScTokenArray > ScTokenArrayRef;
const XclExpCompConfig& mrCfg; /// Configuration for current formula type.
ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array.
XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array.
XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global).
XclExpRefLog* mpRefLog; /// Log for external references.
const ScAddress* mpScBasePos; /// Current cell position of the formula.
ScfUInt8Vec maTokVec; /// Byte vector containing token data.
ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
XclExpOperandListVector maOpListVec; /// Formula structure, maps operators to their operands.
ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator.
bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token.
bool mbVolatile; /// True = Formula contains volatile function.
bool mbOk; /// Current state of the compiler.
explicit XclExpCompData( const XclExpCompConfig* pCfg );
};
XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) :
mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ),
mpLinkMgr( 0 ),
mpRefLog( 0 ),
mpScBasePos( 0 ),
mbStopAtSep( false ),
mbVolatile( false ),
mbOk( pCfg != 0 )
{
DBG_ASSERT( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" );
}
} // namespace
// ----------------------------------------------------------------------------
/** Implementation class of the export formula compiler. */
class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper
{
public:
explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot );
/** Creates an Excel token array from the passed Calc token array. */
XclTokenArrayRef CreateFormula(
XclFormulaType eType, const ScTokenArray& rScTokArr,
const ScAddress* pScBasePos = 0, XclExpRefLog* pRefLog = 0 );
/** Creates a single error token containing the passed error code. */
XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
/** Creates a single token for a special cell reference. */
XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
/** Creates a single tNameXR token for a reference to an external name. */
XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
/** Returns true, if the passed formula type allows 3D references only. */
bool Is3DRefOnly( XclFormulaType eType ) const;
// ------------------------------------------------------------------------
private:
const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
inline sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); }
void Init( XclFormulaType eType );
void Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
const ScAddress* pScBasePos, XclExpRefLog* pRefLog );
void RecalcTokenClasses();
void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass );
void FinalizeFormula();
XclTokenArrayRef CreateTokenArray();
// compiler ---------------------------------------------------------------
// XclExpScToken: pass-by-value and return-by-value is intended
const FormulaToken* GetNextRawToken();
const FormulaToken* PeekNextRawToken( bool bSkipSpaces ) const;
bool GetNextToken( XclExpScToken& rTokData );
XclExpScToken GetNextToken();
XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep );
XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep );
XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses );
XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp );
XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp );
XclExpScToken Factor( XclExpScToken aTokData );
// formula structure ------------------------------------------------------
void ProcessDouble( const XclExpScToken& rTokData );
void ProcessString( const XclExpScToken& rTokData );
void ProcessError( const XclExpScToken& rTokData );
void ProcessMissing( const XclExpScToken& rTokData );
void ProcessBad( const XclExpScToken& rTokData );
void ProcessParentheses( const XclExpScToken& rTokData );
void ProcessBoolean( const XclExpScToken& rTokData );
void ProcessDdeLink( const XclExpScToken& rTokData );
void ProcessExternal( const XclExpScToken& rTokData );
void ProcessMatrix( const XclExpScToken& rTokData );
void ProcessFunction( const XclExpScToken& rTokData );
void PrepareFunction( XclExpFuncData& rFuncData );
void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces );
void FinishIfFunction( XclExpFuncData& rFuncData );
void FinishChooseFunction( XclExpFuncData& rFuncData );
XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData );
void PrepareParam( XclExpFuncData& rFuncData );
void FinishParam( XclExpFuncData& rFuncData );
void AppendDefaultParam( XclExpFuncData& rFuncData );
void AppendTrailingParam( XclExpFuncData& rFuncData );
// reference handling -----------------------------------------------------
SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
bool IsRef2D( const ScSingleRefData& rRefData ) const;
bool IsRef2D( const ScComplexRefData& rRefData ) const;
void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange,
bool bNatLangRef ) const;
XclExpRefLogEntry* GetNewRefLogEntry();
void ProcessCellRef( const XclExpScToken& rTokData );
void ProcessRangeRef( const XclExpScToken& rTokData );
void ProcessExternalCellRef( const XclExpScToken& rTokData );
void ProcessExternalRangeRef( const XclExpScToken& rTokData );
void ProcessDefinedName( const XclExpScToken& rTokData );
void ProcessExternalName( const XclExpScToken& rTokData );
void ProcessDatabaseArea( const XclExpScToken& rTokData );
// token vector -----------------------------------------------------------
void PushOperandPos( sal_uInt16 nTokPos );
void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands );
sal_uInt16 PopOperandPos();
void Append( sal_uInt8 nData );
void Append( sal_uInt8 nData, size_t nCount );
void Append( sal_uInt16 nData );
void Append( sal_uInt32 nData );
void Append( double fData );
void Append( const String& rString );
void AppendAddress( const XclAddress& rXclPos );
void AppendRange( const XclRange& rXclRange );
void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount );
void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 );
void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 );
void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 );
void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 );
void AppendMissingToken( sal_uInt8 nSpaces = 0 );
void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 );
void AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces = 0 );
void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 );
void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 );
void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 );
void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces = 0 );
void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 );
void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 );
void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount );
void AppendFuncToken( const XclExpFuncData& rFuncData );
void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 );
void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType );
void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize );
void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset );
void UpdateAttrGoto( sal_uInt16 nAttrPos );
bool IsSpaceToken( sal_uInt16 nPos ) const;
void RemoveTrailingParen();
void AppendExt( sal_uInt8 nData );
void AppendExt( sal_uInt8 nData, size_t nCount );
void AppendExt( sal_uInt16 nData );
void AppendExt( sal_uInt32 nData );
void AppendExt( double fData );
void AppendExt( const String& rString );
// ------------------------------------------------------------------------
private:
typedef ::std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap;
typedef ScfRef< XclExpCompData > XclExpCompDataRef;
typedef ::std::vector< XclExpCompDataRef > XclExpCompDataVector;
XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types.
XclFunctionProvider maFuncProv; /// Excel function data provider.
XclExpCompDataRef mxData; /// Working data for current formula.
XclExpCompDataVector maDataStack; /// Stack for working data, when compiler is called recursively.
const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls.
const SCsCOL mnMaxAbsCol; /// Maximum column index.
const SCsROW mnMaxAbsRow; /// Maximum row index.
const SCsCOL mnMaxScCol; /// Maximum column index in Calc itself.
const SCsROW mnMaxScRow; /// Maximum row index in Calc itself.
const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields.
const sal_uInt16 mnMaxRowMask; /// Mask to delete invalid bits in row fields.
};
// ----------------------------------------------------------------------------
XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
maFuncProv( rRoot ),
meBiff( rRoot.GetBiff() ),
mnMaxAbsCol( static_cast< SCsCOL >( rRoot.GetXclMaxPos().Col() ) ),
mnMaxAbsRow( static_cast< SCsROW >( rRoot.GetXclMaxPos().Row() ) ),
mnMaxScCol( static_cast< SCsCOL >( rRoot.GetScMaxPos().Col() ) ),
mnMaxScRow( static_cast< SCsROW >( rRoot.GetScMaxPos().Row() ) ),
mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ),
mnMaxRowMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Row() ) )
{
// build the configuration map
for( const XclExpCompConfig* pEntry = spConfigTable; pEntry != STATIC_ARRAY_END( spConfigTable ); ++pEntry )
maCfgMap[ pEntry->meType ] = *pEntry;
}
XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType,
const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
{
// initialize the compiler
Init( eType, rScTokArr, pScBasePos, pRefLog );
// start compilation, if initialization didn't fail
if( mxData->mbOk )
{
XclExpScToken aTokData( GetNextToken() );
sal_uInt16 nScError = rScTokArr.GetCodeError();
if( (nScError != 0) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) )
{
// #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!)
AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces );
}
else if( aTokData.Is() )
{
aTokData = Expression( aTokData, false, false );
}
else
{
DBG_ERRORFILE( "XclExpFmlaCompImpl::CreateFormula - empty token array" );
mxData->mbOk = false;
}
if( mxData->mbOk )
{
// #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token
mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop);
DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" );
}
}
// finalize (add tAttrVolatile token, calculate all token classes)
RecalcTokenClasses();
FinalizeFormula();
// leave recursive call, create and return the final token array
return CreateTokenArray();
}
XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode )
{
Init( EXC_FMLATYPE_NAME );
AppendErrorToken( nErrCode );
return CreateTokenArray();
}
XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos )
{
Init( EXC_FMLATYPE_NAME );
AppendOperandTokenId( nTokenId );
Append( rXclPos.mnRow );
Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here
return CreateTokenArray();
}
XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName )
{
Init( EXC_FMLATYPE_NAME );
AppendNameXToken( nExtSheet, nExtName );
return CreateTokenArray();
}
bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const
{
const XclExpCompConfig* pCfg = GetConfigForType( eType );
return pCfg && pCfg->mb3DRefOnly;
}
// private --------------------------------------------------------------------
const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const
{
XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType );
DBG_ASSERT( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" );
return (aIt == maCfgMap.end()) ? 0 : &aIt->second;
}
void XclExpFmlaCompImpl::Init( XclFormulaType eType )
{
// compiler invoked recursively? - store old working data
if( mxData.get() )
maDataStack.push_back( mxData );
// new compiler working data structure
mxData.reset( new XclExpCompData( GetConfigForType( eType ) ) );
}
void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
{
// common initialization
Init( eType );
// special initialization
if( mxData->mbOk ) switch( mxData->mrCfg.meType )
{
case EXC_FMLATYPE_CELL:
case EXC_FMLATYPE_MATRIX:
case EXC_FMLATYPE_CHART:
mxData->mbOk = pScBasePos != 0;
DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" );
mxData->mpScBasePos = pScBasePos;
break;
case EXC_FMLATYPE_SHARED:
mxData->mbOk = pScBasePos != 0;
DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" );
// clone the passed token array, convert references relative to current cell position
mxData->mxOwnScTokArr.reset( rScTokArr.Clone() );
ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDocPtr(), *pScBasePos, MAXCOL, MAXROW );
// don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs
break;
default:;
}
if( mxData->mbOk )
{
// link manager to be used
mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager();
// token array iterator (use cloned token array if present)
mxData->maTokArrIt.Init( mxData->mxOwnScTokArr.is() ? *mxData->mxOwnScTokArr : rScTokArr, false );
mxData->mpRefLog = pRefLog;
}
}
void XclExpFmlaCompImpl::RecalcTokenClasses()
{
if( mxData->mbOk )
{
mxData->mbOk = mxData->maOpPosStack.size() == 1;
DBG_ASSERT( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" );
if( mxData->mbOk )
{
/* Cell and array formulas start with VAL conversion and VALTYPE
parameter type, defined names start with ARR conversion and
REFTYPE parameter type for the root token. */
XclExpOperandList aOperands;
bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME;
XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL;
XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL;
XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla };
RecalcTokenClass( aConvInfo, eParamConv, eClassConv, bNameFmla );
}
// clear operand vectors (calls to the expensive InsertZeros() may follow)
mxData->maOpListVec.clear();
mxData->maOpPosStack.clear();
}
}
void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo,
XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass )
{
DBG_ASSERT( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" );
sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ];
sal_uInt8 nTokClass = GetTokenClass( rnTokenId );
// REF tokens in VALTYPE parameters behave like VAL tokens
if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) )
ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL );
// replace RPO conversion of operator with parent conversion
XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv;
// find the effective token class conversion to be performed for this token
XclExpClassConv eClassConv = EXC_CLASSCONV_ORG;
switch( eConv )
{
case EXC_PARAMCONV_ORG:
// conversion is forced independent of parent conversion
eClassConv = EXC_CLASSCONV_ORG;
break;
case EXC_PARAMCONV_VAL:
// conversion is forced independent of parent conversion
eClassConv = EXC_CLASSCONV_VAL;
break;
case EXC_PARAMCONV_ARR:
// conversion is forced independent of parent conversion
eClassConv = EXC_CLASSCONV_ARR;
break;
case EXC_PARAMCONV_RPT:
switch( ePrevConv )
{
case EXC_PARAMCONV_ORG:
case EXC_PARAMCONV_VAL:
case EXC_PARAMCONV_ARR:
/* If parent token has REF class (REF token in REFTYPE
function parameter), then RPT does not repeat the
previous explicit ORG or ARR conversion, but always
falls back to VAL conversion. */
eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv;
break;
case EXC_PARAMCONV_RPT:
// nested RPT repeats the previous effective conversion
eClassConv = ePrevClassConv;
break;
case EXC_PARAMCONV_RPX:
/* If parent token has REF class (REF token in REFTYPE
function parameter), then RPX repeats the previous
effective conversion (wich will be either ORG or ARR,
but never VAL), otherwise falls back to ORG conversion. */
eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG;
break;
case EXC_PARAMCONV_RPO: // does not occur
break;
}
break;
case EXC_PARAMCONV_RPX:
/* If current token still has REF class, set previous effective
conversion as current conversion. This will not have an effect
on the REF token but is needed for RPT parameters of this
function that want to repeat this conversion type. If current
token is VAL or ARR class, the previous ARR conversion will be
repeated on the token, but VAL conversion will not. */
eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ?
ePrevClassConv : EXC_CLASSCONV_ORG;
break;
case EXC_PARAMCONV_RPO: // does not occur (see above)
break;
}
// do the token class conversion
switch( eClassConv )
{
case EXC_CLASSCONV_ORG:
/* Cell formulas: leave the current token class. Cell formulas
are the only type of formulas where all tokens can keep
their original token class.
Array and defined name formulas: convert VAL to ARR. */
if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) )
ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR );
break;
case EXC_CLASSCONV_VAL:
// convert ARR to VAL
if( nTokClass == EXC_TOKCLASS_ARR )
ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_VAL );
break;
case EXC_CLASSCONV_ARR:
// convert VAL to ARR
if( nTokClass == EXC_TOKCLASS_VAL )
ChangeTokenClass( rnTokenId, nTokClass = EXC_TOKCLASS_ARR );
break;
}
// do conversion for nested operands, if token is an operator or function
if( rConvInfo.mnTokPos < mxData->maOpListVec.size() )
if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() )
for( XclExpOperandList::const_iterator aIt = pOperands->begin(), aEnd = pOperands->end(); aIt != aEnd; ++aIt )
RecalcTokenClass( *aIt, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF );
}
void XclExpFmlaCompImpl::FinalizeFormula()
{
if( mxData->mbOk )
{
// Volatile? Add a tAttrVolatile token at the beginning of the token array.
if( mxData->mbVolatile )
{
// tAttrSpace token can be extended with volatile flag
if( !IsSpaceToken( 0 ) )
{
InsertZeros( 0, 4 );
mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR;
}
mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE;
}
// Token array too long? -> error
mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN;
}
if( !mxData->mbOk )
{
// Any unrecoverable error? -> Create a =#NA formula.
mxData->maTokVec.clear();
mxData->maExtDataVec.clear();
mxData->mbVolatile = false;
AppendErrorToken( EXC_ERR_NA );
}
}
XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray()
{
// create the Excel token array from working data before resetting mxData
DBG_ASSERT( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" );
if( !mxData->mrCfg.mbAllowArrays )
mxData->maExtDataVec.clear();
XclTokenArrayRef xTokArr( new XclTokenArray( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile ) );
mxData.reset();
// compiler invoked recursively? - restore old working data
if( !maDataStack.empty() )
{
mxData = maDataStack.back();
maDataStack.pop_back();
}
return xTokArr;
}
// compiler -------------------------------------------------------------------
const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken()
{
const FormulaToken* pScToken = mxData->maTokArrIt.Get();
++mxData->maTokArrIt;
return pScToken;
}
const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken( bool bSkipSpaces ) const
{
/* Returns pointer to next raw token in the token array. The token array
iterator already points to the next token (A call to GetNextToken()
always increases the iterator), so this function just returns the token
the iterator points to. To skip space tokens, a copy of the iterator is
created and set to the passed skip-spaces mode. If spaces have to be
skipped, and the iterator currently points to a space token, the
constructor will move it to the next non-space token. */
XclTokenArrayIterator aTempIt( mxData->maTokArrIt, bSkipSpaces );
return aTempIt.Get();
}
bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
{
rTokData.mpScToken = GetNextRawToken();
rTokData.mnSpaces = (rTokData.GetOpCode() == ocSpaces) ? rTokData.mpScToken->GetByte() : 0;
while( rTokData.GetOpCode() == ocSpaces )
rTokData.mpScToken = GetNextRawToken();
return rTokData.Is();
}
XclExpScToken XclExpFmlaCompImpl::GetNextToken()
{
XclExpScToken aTokData;
GetNextToken( aTokData );
return aTokData;
}
namespace {
/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetCompareTokenId( OpCode eOpCode )
{
switch( eOpCode )
{
case ocLess: return EXC_TOKID_LT;
case ocLessEqual: return EXC_TOKID_LE;
case ocEqual: return EXC_TOKID_EQ;
case ocGreaterEqual: return EXC_TOKID_GE;
case ocGreater: return EXC_TOKID_GT;
case ocNotEqual: return EXC_TOKID_NE;
default:;
}
return EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetConcatTokenId( OpCode eOpCode )
{
return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE;
}
/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode )
{
switch( eOpCode )
{
case ocAdd: return EXC_TOKID_ADD;
case ocSub: return EXC_TOKID_SUB;
default:;
}
return EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode )
{
switch( eOpCode )
{
case ocMul: return EXC_TOKID_MUL;
case ocDiv: return EXC_TOKID_DIV;
default:;
}
return EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetPowTokenId( OpCode eOpCode )
{
return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode )
{
return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode )
{
switch( eOpCode )
{
case ocAdd: return EXC_TOKID_UPLUS; // +(1)
case ocNeg: return EXC_TOKID_UMINUS; // NEG(1)
case ocNegSub: return EXC_TOKID_UMINUS; // -(1)
default:;
}
return EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep )
{
return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode )
{
return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE;
}
/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */
inline sal_uInt8 lclGetRangeTokenId( OpCode eOpCode )
{
return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE;
}
} // namespace
XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep )
{
if( mxData->mbOk && aTokData.Is() )
{
// remember old stop-at-ocSep mode, restored below
bool bOldStopAtSep = mxData->mbStopAtSep;
mxData->mbStopAtSep = bStopAtSep;
// start compilation of the subexpression
aTokData = OrTerm( aTokData, bInParentheses );
// restore old stop-at-ocSep mode
mxData->mbStopAtSep = bOldStopAtSep;
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep )
{
while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) )
{
if( aTokData.GetOpCode() == ocOpen )
{
aTokData = SkipExpression( GetNextToken(), false );
if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose;
}
aTokData = GetNextToken();
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = AndTerm( aTokData, bInParentheses );
sal_uInt8 nParamCount = 1;
while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) )
{
RemoveTrailingParen();
aTokData = AndTerm( GetNextToken(), bInParentheses );
RemoveTrailingParen();
++nParamCount;
if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
}
if( mxData->mbOk && (nParamCount > 1) )
AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount );
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = CompareTerm( aTokData, bInParentheses );
sal_uInt8 nParamCount = 1;
while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) )
{
RemoveTrailingParen();
aTokData = CompareTerm( GetNextToken(), bInParentheses );
RemoveTrailingParen();
++nParamCount;
if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
}
if( mxData->mbOk && (nParamCount > 1) )
AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount );
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = ConcatTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = ConcatTerm( GetNextToken(), bInParentheses );
AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = AddSubTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = AddSubTerm( GetNextToken(), bInParentheses );
AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = MulDivTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = MulDivTerm( GetNextToken(), bInParentheses );
AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = PowTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = PowTerm( GetNextToken(), bInParentheses );
AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = UnaryPostTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = UnaryPostTerm( GetNextToken(), bInParentheses );
AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses )
{
aTokData = UnaryPreTerm( aTokData, bInParentheses );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces );
GetNextToken( aTokData );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses )
{
sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE;
if( nOpTokenId != EXC_TOKID_NONE )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = UnaryPreTerm( GetNextToken(), bInParentheses );
AppendUnaryOperatorToken( nOpTokenId, nSpaces );
}
else
{
aTokData = ListTerm( aTokData, bInParentheses );
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses )
{
sal_uInt16 nSubExprPos = GetSize();
bool bHasAnyRefOp = false;
bool bHasListOp = false;
aTokData = IntersectTerm( aTokData, bHasAnyRefOp );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp );
AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
bHasAnyRefOp = bHasListOp = true;
}
if( bHasAnyRefOp )
{
// add a tMemFunc token enclosing the entire reference subexpression
sal_uInt16 nSubExprSize = GetSize() - nSubExprPos;
InsertZeros( nSubExprPos, 3 );
mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF );
Overwrite( nSubExprPos + 1, nSubExprSize );
// update the operand/operator stack (set the list expression as operand of the tMemFunc)
XclExpOperandListRef xOperands( new XclExpOperandList );
xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false );
PushOperatorPos( nSubExprPos, xOperands );
}
// #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2))
if( bHasListOp && !bInParentheses )
AppendParenToken();
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp )
{
aTokData = RangeTerm( aTokData, rbHasRefOp );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = RangeTerm( GetNextToken(), rbHasRefOp );
AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
rbHasRefOp = true;
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp )
{
aTokData = Factor( aTokData );
sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
while( mxData->mbOk && ((nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() )) != EXC_TOKID_NONE) )
{
sal_uInt8 nSpaces = aTokData.mnSpaces;
aTokData = Factor( GetNextToken() );
AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
rbHasRefOp = true;
}
return aTokData;
}
XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData )
{
if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken();
switch( aTokData.GetType() )
{
case svUnknown: mxData->mbOk = false; break;
case svDouble: ProcessDouble( aTokData ); break;
case svString: ProcessString( aTokData ); break;
#if 0 // erAck
case svError: ProcessError( aTokData ); break;
#endif
case svSingleRef: ProcessCellRef( aTokData ); break;
case svDoubleRef: ProcessRangeRef( aTokData ); break;
case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break;
case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break;
case svExternalName: ProcessExternalName( aTokData ); break;
case svMatrix: ProcessMatrix( aTokData ); break;
case svExternal: ProcessExternal( aTokData ); break;
default: switch( aTokData.GetOpCode() )
{
case ocNone: /* do nothing */ break;
case ocMissing: ProcessMissing( aTokData ); break;
case ocBad: ProcessBad( aTokData ); break;
case ocOpen: ProcessParentheses( aTokData ); break;
case ocName: ProcessDefinedName( aTokData ); break;
case ocDBArea: ProcessDatabaseArea( aTokData ); break;
case ocFalse:
case ocTrue: ProcessBoolean( aTokData ); break;
case ocDde: ProcessDdeLink( aTokData ); break;
default: ProcessFunction( aTokData );
}
}
return GetNextToken();
}
// formula structure ----------------------------------------------------------
void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData )
{
double fValue = rTokData.mpScToken->GetDouble();
double fInt;
double fFrac = modf( fValue, &fInt );
if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) )
AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces );
else
AppendNumToken( fValue, rTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData )
{
AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces );
Append( rTokData.mpScToken->GetString() );
}
void XclExpFmlaCompImpl::ProcessError( const XclExpScToken& rTokData )
{
#if 0 // erAck
AppendErrorToken( XclTools::GetXclErrorCode( rTokData.mpScToken->GetError() ), rTokData.mnSpaces );
#else
(void)rTokData; // compiler warning
#endif
}
void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData )
{
AppendMissingToken( rTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData )
{
AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData )
{
XclExpScToken aTokData = Expression( GetNextToken(), true, false );
mxData->mbOk = aTokData.GetOpCode() == ocClose;
AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData )
{
mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
if( mxData->mbOk )
AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces );
}
namespace {
inline bool lclGetTokenString( String& rString, const XclExpScToken& rTokData )
{
bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush);
if( bIsStr )
rString = rTokData.mpScToken->GetString();
return bIsStr;
}
} // namespace
void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData )
{
String aApplic, aTopic, aItem;
mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() );
if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() );
if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() );
if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
if( mxData->mbOk ) mxData->mbOk = aApplic.Len() && aTopic.Len() && aItem.Len();
if( mxData->mbOk )
{
sal_uInt16 nExtSheet, nExtName;
if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) )
AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
else
AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
}
}
void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData )
{
/* #i47228# Excel import generates svExternal/ocMacro tokens for invalid
names and for external/invalid function calls. This function looks for
the next token in the token array. If it is an opening parenthesis, the
token is processed as external function call, otherwise as undefined name. */
const FormulaToken* pNextScToken = PeekNextRawToken( true );
if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) )
AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces );
else
ProcessFunction( rTokData );
}
void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData )
{
const ScMatrix* pMatrix = static_cast< const ScToken* >( rTokData.mpScToken )->GetMatrix();
if( pMatrix && mxData->mrCfg.mbAllowArrays )
{
SCSIZE nScCols, nScRows;
pMatrix->GetDimensions( nScCols, nScRows );
DBG_ASSERT( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" );
sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 );
sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 );
// create the tArray token
AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces );
Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
Append( static_cast< sal_uInt32 >( 0 ) );
// create the extended data containing the array values
AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow )
{
for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol )
{
ScMatValType nType;
const ScMatrixValue* pMatVal = pMatrix->Get( nScCol, nScRow, nType );
DBG_ASSERT( pMatVal, "XclExpFmlaCompImpl::ProcessMatrix - missing matrix value" );
if( ScMatrix::IsValueType( nType ) ) // value, boolean, or error
{
if( ScMatrix::IsBooleanType( nType ) )
{
AppendExt( EXC_CACHEDVAL_BOOL );
AppendExt( static_cast< sal_uInt8 >( pMatVal->GetBoolean() ? 1 : 0 ) );
AppendExt( 0, 7 );
}
else if( sal_uInt16 nErr = pMatVal->GetError() )
{
AppendExt( EXC_CACHEDVAL_ERROR );
AppendExt( XclTools::GetXclErrorCode( nErr ) );
AppendExt( 0, 7 );
}
else
{
AppendExt( EXC_CACHEDVAL_DOUBLE );
AppendExt( pMatVal->fVal );
}
}
else // string or empty
{
const String& rStr = pMatVal->GetString();
if( rStr.Len() == 0 )
{
AppendExt( EXC_CACHEDVAL_EMPTY );
AppendExt( 0, 8 );
}
else
{
AppendExt( EXC_CACHEDVAL_STRING );
AppendExt( rStr );
}
}
}
}
}
else
{
// array in places that do not allow it (cond fmts, data validation)
AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
}
}
void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData )
{
OpCode eOpCode = rTokData.GetOpCode();
const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode );
XclExpExtFuncData aExtFuncData;
// no exportable function found - try to create an external macro call
if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) )
{
const String& rFuncName = ScCompiler::GetNativeSymbol( eOpCode );
if( rFuncName.Len() )
{
aExtFuncData.Set( rFuncName, true, false );
pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro );
}
}
mxData->mbOk = pFuncInfo != 0;
if( !mxData->mbOk ) return;
// functions simulated by a macro call in file format
if( pFuncInfo->IsMacroFunc() )
aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true );
XclExpFuncData aFuncData( rTokData, *pFuncInfo, aExtFuncData );
XclExpScToken aTokData;
// preparations for special functions, before function processing starts
PrepareFunction( aFuncData );
enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END }
eState = STATE_START;
while( eState != STATE_END ) switch( eState )
{
case STATE_START:
mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen);
eState = mxData->mbOk ? STATE_OPEN : STATE_END;
break;
case STATE_OPEN:
mxData->mbOk = GetNextToken( aTokData );
eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END;
break;
case STATE_PARAM:
aTokData = ProcessParam( aTokData, aFuncData );
switch( aTokData.GetOpCode() )
{
case ocSep: eState = STATE_SEP; break;
case ocClose: eState = STATE_CLOSE; break;
default: mxData->mbOk = false;
}
if( !mxData->mbOk ) eState = STATE_END;
break;
case STATE_SEP:
mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData );
eState = mxData->mbOk ? STATE_PARAM : STATE_END;
break;
case STATE_CLOSE:
FinishFunction( aFuncData, aTokData.mnSpaces );
eState = STATE_END;
break;
default:;
}
}
void XclExpFmlaCompImpl::PrepareFunction( XclExpFuncData& rFuncData )
{
switch( rFuncData.GetOpCode() )
{
case ocCosecant: // simulate CSC(x) by (1/SIN(x))
case ocSecant: // simulate SEC(x) by (1/COS(x))
case ocCot: // simulate COT(x) by (1/TAN(x))
case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
AppendIntToken( 1 );
break;
case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
AppendNumToken( F_PI2 );
break;
default:;
}
}
void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces )
{
// append missing parameters required in Excel, may modify param count
AppendTrailingParam( rFuncData );
// check if parameter count fits into the limits of the function
sal_uInt8 nParamCount = rFuncData.GetParamCount();
if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) )
{
// first put the tAttrSpace tokens, they must not be included in tAttrGoto handling
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() );
// add tAttrGoto tokens for IF or CHOOSE functions
switch( rFuncData.GetOpCode() )
{
case ocIf:
case ocChose:
AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
break;
default:;
}
// put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum)
AppendFuncToken( rFuncData );
// update volatile flag - is set if at least one used function is volatile
mxData->mbVolatile |= rFuncData.IsVolatile();
// update jump tokens for specific functions, add additional tokens
switch( rFuncData.GetOpCode() )
{
case ocIf:
FinishIfFunction( rFuncData );
break;
case ocChose:
FinishChooseFunction( rFuncData );
break;
case ocCosecant: // simulate CSC(x) by (1/SIN(x))
case ocSecant: // simulate SEC(x) by (1/COS(x))
case ocCot: // simulate COT(x) by (1/TAN(x))
case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
AppendParenToken();
break;
case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
AppendBinaryOperatorToken( EXC_TOKID_SUB, true );
AppendParenToken();
break;
default:;
}
}
else
mxData->mbOk = false;
}
void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData )
{
sal_uInt16 nParamCount = rFuncData.GetParamCount();
DBG_ASSERT( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" );
const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" );
// update tAttrIf token following the condition parameter
Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) );
// update the tAttrGoto tokens following true and false parameters
UpdateAttrGoto( rAttrPos[ 1 ] );
if( nParamCount == 3 )
UpdateAttrGoto( rAttrPos[ 2 ] );
}
void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData )
{
sal_uInt16 nParamCount = rFuncData.GetParamCount();
ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
DBG_ASSERT( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" );
// number of choices is parameter count minus 1
sal_uInt16 nChoices = nParamCount - 1;
// tAttrChoose token contains number of choices
Overwrite( rAttrPos[ 0 ] + 2, nChoices );
// cache position of the jump table (follows number of choices in tAttrChoose token)
sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4;
// size of jump table: number of choices, plus 1 for error position
sal_uInt16 nJumpArrSize = 2 * (nChoices + 1);
// insert the jump table into the tAttrChoose token
InsertZeros( nJumpArrPos, nJumpArrSize );
// update positions of tAttrGoto tokens after jump table insertion
sal_uInt16 nIdx;
for( nIdx = 1; nIdx < nParamCount; ++nIdx )
rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize;
// update the tAttrGoto tokens (they contain a value one-less to real distance)
for( nIdx = 1; nIdx < nParamCount; ++nIdx )
UpdateAttrGoto( rAttrPos[ nIdx ] );
// update the distances in the jump table
Overwrite( nJumpArrPos, nJumpArrSize );
for( nIdx = 1; nIdx < nParamCount; ++nIdx )
Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) );
}
XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData )
{
if( rFuncData.IsCalcOnlyParam() )
{
// skip Calc-only parameter, stop at next ocClose or ocSep
aTokData = SkipExpression( aTokData, true );
rFuncData.IncParamInfoIdx();
}
else
{
// insert Excel-only parameters, modifies param count and class in rFuncData
while( rFuncData.IsExcelOnlyParam() )
AppendDefaultParam( rFuncData );
// process the parameter, stop at next ocClose or ocSep
PrepareParam( rFuncData );
/* #i37355# insert tMissArg token for missing parameters --
Excel import filter adds ocMissing token (handled in Factor()),
but Calc itself does not do this if a new formula is entered. */
switch( aTokData.GetOpCode() )
{
case ocSep:
case ocClose: AppendMissingToken(); break; // empty parameter
default: aTokData = Expression( aTokData, false, true );
}
// finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters
if( mxData->mbOk ) FinishParam( rFuncData );
}
return aTokData;
}
void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData )
{
// index of this parameter is equal to number of already finished parameters
sal_uInt8 nParamIdx = rFuncData.GetParamCount();
switch( rFuncData.GetOpCode() )
{
case ocIf:
switch( nParamIdx )
{
// add a tAttrIf token before true-parameter (second parameter)
case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break;
// add a tAttrGoto token before false-parameter (third parameter)
case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break;
}
break;
case ocChose:
switch( nParamIdx )
{
// do nothing for first parameter
case 0: break;
// add a tAttrChoose token before first value parameter (second parameter)
case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break;
// add a tAttrGoto token before other value parameters
default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
}
break;
case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
if( nParamIdx == 0 )
AppendIntToken( 1 );
break;
default:;
}
}
void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData )
{
// increase parameter count, update operand stack
rFuncData.FinishParam( PopOperandPos() );
// append more tokens for parameters of some special functions
sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1;
switch( rFuncData.GetOpCode() )
{
case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
if( nParamIdx == 0 )
{
AppendParenToken();
AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
}
break;
default:;
}
}
void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData )
{
// prepare parameters of some special functions
PrepareParam( rFuncData );
switch( rFuncData.GetOpCode() )
{
case ocExternal:
AppendAddInCallToken( rFuncData.GetExtFuncData() );
break;
case ocEuroConvert:
AppendEuroToolCallToken( rFuncData.GetExtFuncData() );
break;
case ocMacro:
AppendMacroCallToken( rFuncData.GetExtFuncData() );
break;
default:
{
DBG_ASSERT( rFuncData.IsMacroFunc(), "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" );
if( rFuncData.IsMacroFunc() )
AppendMacroCallToken( rFuncData.GetExtFuncData() );
else
AppendMissingToken(); // to keep parameter count valid
}
}
// update parameter count, add special parameter tokens
FinishParam( rFuncData );
}
void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData )
{
sal_uInt8 nParamCount = rFuncData.GetParamCount();
switch( rFuncData.GetOpCode() )
{
case ocIf:
if( nParamCount == 1 )
{
// #112262# Excel needs at least two parameters in IF function
PrepareParam( rFuncData );
AppendBoolToken( true );
FinishParam( rFuncData );
}
break;
case ocRound:
case ocRoundUp:
case ocRoundDown:
if( nParamCount == 1 )
{
// ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
PrepareParam( rFuncData );
AppendIntToken( 0 );
FinishParam( rFuncData );
}
break;
case ocIndex:
if( nParamCount == 1 )
{
// INDEX function needs at least 2 parameters in Excel
PrepareParam( rFuncData );
AppendMissingToken();
FinishParam( rFuncData );
}
break;
case ocExternal:
case ocMacro:
// external or macro call without parameters needs the external name reference
if( nParamCount == 0 )
AppendDefaultParam( rFuncData );
break;
case ocGammaDist:
if( nParamCount == 3 )
{
// GAMMADIST function needs 4 parameters in Excel
PrepareParam( rFuncData );
AppendIntToken( 1 );
FinishParam( rFuncData );
}
break;
case ocPoissonDist:
if( nParamCount == 2 )
{
// POISSON function needs 3 parameters in Excel
PrepareParam( rFuncData );
AppendIntToken( 1 );
FinishParam( rFuncData );
}
break;
case ocNormDist:
if( nParamCount == 3 )
{
// NORMDIST function needs 4 parameters in Excel
PrepareParam( rFuncData );
AppendBoolToken( true );
FinishParam( rFuncData );
}
break;
case ocLogNormDist:
switch( nParamCount )
{
// LOGNORMDIST function needs 3 parameters in Excel
case 1:
PrepareParam( rFuncData );
AppendIntToken( 0 );
FinishParam( rFuncData );
// do not break, add next default parameter
case 2:
PrepareParam( rFuncData );
AppendIntToken( 1 );
FinishParam( rFuncData );
break;
default:;
}
break;
default:
// #i108420# function without parameters stored as macro call needs the external name reference
if( (nParamCount == 0) && rFuncData.IsMacroFunc() )
AppendDefaultParam( rFuncData );
}
}
// reference handling ---------------------------------------------------------
namespace {
inline bool lclIsRefRel2D( const ScSingleRefData& rRefData )
{
return rRefData.IsColRel() || rRefData.IsRowRel();
}
inline bool lclIsRefDel2D( const ScSingleRefData& rRefData )
{
return rRefData.IsColDeleted() || rRefData.IsRowDeleted();
}
inline bool lclIsRefRel2D( const ScComplexRefData& rRefData )
{
return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 );
}
inline bool lclIsRefDel2D( const ScComplexRefData& rRefData )
{
return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 );
}
} // namespace
SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
{
bool bInvTab = rRefData.IsTabDeleted() || (!mxData->mpScBasePos && IsInGlobals() && rRefData.IsTabRel());
return bInvTab ? SCTAB_INVALID : static_cast< SCTAB >( rRefData.nTab );
}
bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const
{
/* rRefData.IsFlag3D() determines if sheet name is always visible, even on
the own sheet. If 3D references are allowed, the passed reference does
not count as 2D reference. */
return (!mxData->mpLinkMgr || !rRefData.IsFlag3D()) && !rRefData.IsTabDeleted() &&
(rRefData.IsTabRel() ? (rRefData.nRelTab == 0) : (static_cast< SCTAB >( rRefData.nTab ) == GetCurrScTab()));
}
bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData ) const
{
return IsRef2D( rRefData.Ref1 ) && IsRef2D( rRefData.Ref2 );
}
void XclExpFmlaCompImpl::ConvertRefData(
ScSingleRefData& rRefData, XclAddress& rXclPos,
bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const
{
if( mxData->mpScBasePos )
{
// *** reference position exists (cell, matrix) - convert to absolute ***
rRefData.CalcAbsIfRel( *mxData->mpScBasePos );
// convert column index
SCsCOL& rnScCol = rRefData.nCol;
if( bTruncMaxCol && (rnScCol == mnMaxScCol) )
rnScCol = mnMaxAbsCol;
else if( (rnScCol < 0) || (rnScCol > mnMaxAbsCol) )
rRefData.SetColDeleted( sal_True );
rXclPos.mnCol = static_cast< sal_uInt16 >( rnScCol ) & mnMaxColMask;
// convert row index
SCsROW& rnScRow = rRefData.nRow;
if( bTruncMaxRow && (rnScRow == mnMaxScRow) )
rnScRow = mnMaxAbsRow;
else if( (rnScRow < 0) || (rnScRow > mnMaxAbsRow) )
rRefData.SetRowDeleted( sal_True );
rXclPos.mnRow = static_cast< sal_uInt16 >( rnScRow ) & mnMaxRowMask;
}
else
{
// *** no reference position (shared, names, condfmt) - use relative values ***
// convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly)
sal_Int16 nXclRelCol = static_cast< sal_Int16 >( rRefData.IsColRel() ? rRefData.nRelCol : rRefData.nCol );
rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask;
// convert row index (2-step-cast ScsROW->sal_Int16->sal_uInt16 to get all bits correctly)
sal_Int16 nXclRelRow = static_cast< sal_Int16 >( rRefData.IsRowRel() ? rRefData.nRelRow : rRefData.nRow );
rXclPos.mnRow = static_cast< sal_uInt16 >( nXclRelRow ) & mnMaxRowMask;
// resolve relative tab index if possible
if( rRefData.IsTabRel() && !IsInGlobals() && (GetCurrScTab() < GetDoc().GetTableCount()) )
rRefData.nTab = static_cast< SCsTAB >( GetCurrScTab() + rRefData.nRelTab );
}
// flags for relative column and row
if( bNatLangRef )
{
DBG_ASSERT( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" );
// Calc does not support absolute reference mode in natural language references
::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL );
}
else
{
sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rXclPos.mnRow : rXclPos.mnCol;
::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() );
::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() );
}
}
void XclExpFmlaCompImpl::ConvertRefData(
ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const
{
// convert start and end of the range
ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false );
bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rRefData.Ref1.nCol == 0);
bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rRefData.Ref1.nRow == 0);
ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow );
}
XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry()
{
if( mxData->mpRefLog )
{
mxData->mpRefLog->resize( mxData->mpRefLog->size() + 1 );
return &mxData->mpRefLog->back();
}
return 0;
}
void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
{
// get the Excel address components, adjust internal data in aRefData
bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName);
ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef();
XclAddress aXclPos( ScAddress::UNINITIALIZED );
ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false );
if( bNatLangRef )
{
DBG_ASSERT( aRefData.IsColRel() != aRefData.IsRowRel(),
"XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" );
// create tNlr token for natural language reference
sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV;
AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces );
Append( nSubId );
AppendAddress( aXclPos );
}
else
{
// store external cell contents in CRN records
if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
mxData->mpLinkMgr->StoreCell( aRefData );
// create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token
if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) )
{
// 2D reference (not in defined names, but allowed in range lists)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
(lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF);
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
AppendAddress( aXclPos );
}
else if( mxData->mpLinkMgr ) // 3D reference
{
// 1-based EXTERNSHEET index and 0-based Excel sheet index
sal_uInt16 nExtSheet, nXclTab;
mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() );
// write the token
sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
Append( nExtSheet );
if( meBiff <= EXC_BIFF5 )
{
Append( 0, 8 );
Append( nXclTab );
Append( nXclTab );
}
AppendAddress( aXclPos );
}
else
{
// 3D ref in cond. format, or 2D ref in name
AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
}
}
}
void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
{
// get the Excel address components, adjust internal data in aRefData
ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef();
XclRange aXclRange( ScAddress::UNINITIALIZED );
ConvertRefData( aRefData, aXclRange, false );
// store external cell contents in CRN records
if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
mxData->mpLinkMgr->StoreCellRange( aRefData );
// create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token
if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) )
{
// 2D reference (not in name formulas, but allowed in range lists)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
(lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA);
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
AppendRange( aXclRange );
}
else if( mxData->mpLinkMgr ) // 3D reference
{
// 1-based EXTERNSHEET index and 0-based Excel sheet indexes
sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab;
mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab,
GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() );
// write the token
sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
Append( nExtSheet );
if( meBiff <= EXC_BIFF5 )
{
Append( 0, 8 );
Append( nFirstXclTab );
Append( nLastXclTab );
}
AppendRange( aXclRange );
}
else
{
// 3D ref in cond. format, or 2D ref in name
AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
}
}
void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData )
{
if( mxData->mpLinkMgr )
{
// get the Excel address components, adjust internal data in aRefData
ScSingleRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetSingleRef();
XclAddress aXclPos( ScAddress::UNINITIALIZED );
ConvertRefData( aRefData, aXclPos, false, false, false );
// store external cell contents in CRN records
sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
const String& rTabName = rTokData.mpScToken->GetString();
if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
mxData->mpLinkMgr->StoreCell( nFileId, rTabName, aRefData );
// 1-based EXTERNSHEET index and 0-based Excel sheet indexes
sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() );
// write the token
sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
Append( nExtSheet );
if( meBiff <= EXC_BIFF5 )
{
Append( 0, 8 );
Append( nFirstSBTab );
Append( nLastSBTab );
}
AppendAddress( aXclPos );
}
else
{
AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
}
}
void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData )
{
if( mxData->mpLinkMgr )
{
// get the Excel address components, adjust internal data in aRefData
ScComplexRefData aRefData = static_cast< const ScToken* >( rTokData.mpScToken )->GetDoubleRef();
XclRange aXclRange( ScAddress::UNINITIALIZED );
ConvertRefData( aRefData, aXclRange, false );
// store external cell contents in CRN records
sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
const String& rTabName = rTokData.mpScToken->GetString();
if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
mxData->mpLinkMgr->StoreCellRange( nFileId, rTabName, aRefData );
// 1-based EXTERNSHEET index and 0-based Excel sheet indexes
sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
sal_uInt16 nTabSpan = static_cast< sal_uInt16 >( aRefData.Ref2.nTab - aRefData.Ref1.nTab + 1 );
mxData->mpLinkMgr->FindExtSheet( nFileId, rTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() );
// write the token
sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
Append( nExtSheet );
if( meBiff <= EXC_BIFF5 )
{
Append( 0, 8 );
Append( nFirstSBTab );
Append( nLastSBTab );
}
AppendRange( aXclRange );
}
else
{
AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
}
}
void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData )
{
XclExpNameManager& rNameMgr = GetNameManager();
sal_uInt16 nNameIdx = rNameMgr.InsertName( rTokData.mpScToken->GetIndex() );
if( nNameIdx != 0 )
{
// global names always with tName token, local names dependent on config
SCTAB nScTab = rNameMgr.GetScTab( nNameIdx );
if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) )
{
AppendNameToken( nNameIdx, rTokData.mnSpaces );
}
else if( mxData->mpLinkMgr )
{
// use the same special EXTERNNAME to refer to any local name
sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC );
AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces );
}
else
AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
// volatile names (containing volatile functions)
mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx );
}
else
AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData )
{
if( mxData->mpLinkMgr )
{
ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager();
sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
const String& rName = rTokData.mpScToken->GetString();
ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, rName );
if( xArray.get() )
{
// store external cell contents in CRN records
if( mxData->mpScBasePos )
{
for( FormulaToken* pScToken = xArray->First(); pScToken; pScToken = xArray->Next() )
{
if( pScToken->GetOpCode() == ocExternalRef )
{
switch( pScToken->GetType() )
{
case svExternalSingleRef:
{
ScSingleRefData aRefData = static_cast< ScToken* >( pScToken )->GetSingleRef();
aRefData.CalcAbsIfRel( *mxData->mpScBasePos );
mxData->mpLinkMgr->StoreCell( nFileId, pScToken->GetString(), aRefData );
}
break;
case svExternalDoubleRef:
{
ScComplexRefData aRefData = static_cast< ScToken* >( pScToken )->GetDoubleRef();
aRefData.CalcAbsIfRel( *mxData->mpScBasePos );
mxData->mpLinkMgr->StoreCellRange( nFileId, pScToken->GetString(), aRefData );
}
default:
; // nothing, avoid compiler warning
}
}
}
}
// insert the new external name and create the tNameX token
sal_uInt16 nExtSheet, nExtName;
const String* pFile = rExtRefMgr.getExternalFileName( nFileId );
if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, rName, xArray ) )
{
AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
return;
}
}
}
// on any error: create a #NAME? error
AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
}
void XclExpFmlaCompImpl::ProcessDatabaseArea( const XclExpScToken& rTokData )
{
sal_uInt16 nNameIdx = GetNameManager().InsertDBRange( rTokData.mpScToken->GetIndex() );
AppendNameToken( nNameIdx, rTokData.mnSpaces );
}
// token vector ---------------------------------------------------------------
void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos )
{
mxData->maOpPosStack.push_back( nTokPos );
}
void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands )
{
PushOperandPos( nTokPos );
DBG_ASSERT( rxOperands.get(), "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" );
if( mxData->maOpListVec.size() <= nTokPos )
mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() );
mxData->maOpListVec[ nTokPos ] = rxOperands;
}
sal_uInt16 XclExpFmlaCompImpl::PopOperandPos()
{
DBG_ASSERT( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" );
mxData->mbOk &= !mxData->maOpPosStack.empty();
if( mxData->mbOk )
{
sal_uInt16 nTokPos = mxData->maOpPosStack.back();
mxData->maOpPosStack.pop_back();
return nTokPos;
}
return 0;
}
namespace {
inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData )
{
orVector.resize( orVector.size() + 2 );
ShortToSVBT16( nData, &*(orVector.end() - 2) );
}
inline void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData )
{
orVector.resize( orVector.size() + 4 );
UInt32ToSVBT32( nData, &*(orVector.end() - 4) );
}
inline void lclAppend( ScfUInt8Vec& orVector, double fData )
{
orVector.resize( orVector.size() + 8 );
DoubleToSVBT64( fData, &*(orVector.end() - 8) );
}
inline void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const String& rString, XclStrFlags nStrFlags )
{
XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN );
size_t nSize = orVector.size();
orVector.resize( nSize + xXclStr->GetSize() );
xXclStr->WriteToMem( &orVector[ nSize ] );
}
} // namespace
void XclExpFmlaCompImpl::Append( sal_uInt8 nData )
{
mxData->maTokVec.push_back( nData );
}
void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount )
{
mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData );
}
void XclExpFmlaCompImpl::Append( sal_uInt16 nData )
{
lclAppend( mxData->maTokVec, nData );
}
void XclExpFmlaCompImpl::Append( sal_uInt32 nData )
{
lclAppend( mxData->maTokVec, nData );
}
void XclExpFmlaCompImpl::Append( double fData )
{
lclAppend( mxData->maTokVec, fData );
}
void XclExpFmlaCompImpl::Append( const String& rString )
{
lclAppend( mxData->maTokVec, GetRoot(), rString, EXC_STR_8BITLENGTH );
}
void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos )
{
Append( rXclPos.mnRow );
if( meBiff <= EXC_BIFF5 )
Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) );
else
Append( rXclPos.mnCol );
}
void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange )
{
Append( rXclRange.maFirst.mnRow );
Append( rXclRange.maLast.mnRow );
if( meBiff <= EXC_BIFF5 )
{
Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) );
Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) );
}
else
{
Append( rXclRange.maFirst.mnCol );
Append( rXclRange.maLast.mnCol );
}
}
void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount )
{
if( nCount > 0 )
{
Append( EXC_TOKID_ATTR );
Append( EXC_TOK_ATTR_SPACE );
Append( nType );
Append( nCount );
}
}
void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
{
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
PushOperandPos( GetSize() );
Append( nTokenId );
}
void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces )
{
AppendOperandTokenId( EXC_TOKID_INT, nSpaces );
Append( nValue );
}
void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces )
{
AppendOperandTokenId( EXC_TOKID_NUM, nSpaces );
Append( fValue );
}
void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces )
{
AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces );
Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE );
}
void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces )
{
AppendOperandTokenId( EXC_TOKID_ERR, nSpaces );
Append( nErrCode );
}
void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces )
{
AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces );
}
void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces )
{
if( nNameIdx > 0 )
{
AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces );
Append( nNameIdx );
Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
}
else
AppendErrorToken( EXC_ERR_NAME );
}
void XclExpFmlaCompImpl::AppendMissingNameToken( const String& rName, sal_uInt8 nSpaces )
{
sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName );
AppendNameToken( nNameIdx, nSpaces );
}
void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces )
{
AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces );
Append( nExtSheet );
if( meBiff <= EXC_BIFF5 )
Append( 0, 8 );
Append( nExtName );
Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
}
void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces )
{
sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden );
AppendNameToken( nNameIdx, nSpaces );
}
void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces )
{
String aXclFuncName;
if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) )
{
sal_uInt16 nExtSheet, nExtName;
if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) )
{
AppendNameXToken( nExtSheet, nExtName, nSpaces );
return;
}
}
AppendMacroCallToken( rExtFuncData, nSpaces );
}
void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData, sal_uInt8 nSpaces )
{
sal_uInt16 nExtSheet, nExtName;
if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) )
AppendNameXToken( nExtSheet, nExtName, nSpaces );
else
AppendMacroCallToken( rExtFuncData, nSpaces );
}
void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces )
{
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
PushOperatorPos( GetSize(), rxOperands );
Append( nTokenId );
}
void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
{
XclExpOperandListRef xOperands( new XclExpOperandList );
xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true );
AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
}
void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces )
{
XclExpOperandListRef xOperands( new XclExpOperandList );
xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
}
void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount )
{
XclExpOperandListRef xOperands( new XclExpOperandList );
for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx )
xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false );
AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands );
Append( nOpCount );
Append( nXclFuncIdx );
}
void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData )
{
sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx();
sal_uInt8 nParamCount = rFuncData.GetParamCount();
sal_uInt8 nRetClass = rFuncData.GetReturnClass();
if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) )
{
// SUM with only one parameter
AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() );
Append( EXC_TOK_ATTR_SUM );
Append( sal_uInt16( 0 ) );
}
else if( rFuncData.IsFixedParamCount() )
{
// fixed number of parameters
AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() );
Append( nXclFuncIdx );
}
else
{
// variable number of parameters
AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() );
Append( nParamCount );
Append( nXclFuncIdx );
}
}
void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces )
{
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces );
AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
Append( EXC_TOKID_PAREN );
}
void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType )
{
// store the start position of the token
rFuncData.AppendAttrPos( GetSize() );
// create the tAttr token
Append( EXC_TOKID_ATTR );
Append( nAttrType );
Append( sal_uInt16( 0 ) ); // placeholder that will be updated later
}
void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize )
{
// insert zeros into the token array
DBG_ASSERT( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" );
mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 );
// update positions of operands waiting for an operator
for( ScfUInt16Vec::iterator aIt = mxData->maOpPosStack.begin(), aEnd = mxData->maOpPosStack.end(); aIt != aEnd; ++aIt )
if( nInsertPos <= *aIt )
*aIt = *aIt + nInsertSize;
// update operand lists of all operator tokens
if( nInsertPos < mxData->maOpListVec.size() )
mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() );
for( XclExpOperandListVector::iterator aIt = mxData->maOpListVec.begin(), aEnd = mxData->maOpListVec.end(); aIt != aEnd; ++aIt )
if( aIt->get() )
for( XclExpOperandList::iterator aIt2 = (*aIt)->begin(), aEnd2 = (*aIt)->end(); aIt2 != aEnd2; ++aIt2 )
if( nInsertPos <= aIt2->mnTokPos )
aIt2->mnTokPos = aIt2->mnTokPos + nInsertSize;
}
void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset )
{
DBG_ASSERT( static_cast< size_t >( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" );
ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] );
}
void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos )
{
/* tAttrGoto contains distance from end of tAttr token to position behind
the function token (for IF or CHOOSE function), which is currently at
the end of the token array. Additionally this distance is decreased by
one, for whatever reason. So we have to subtract 4 and 1 from the
distance between the tAttr token start and the end of the token array. */
Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) );
}
bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const
{
return
(static_cast< size_t >( nPos + 4 ) <= mxData->maTokVec.size()) &&
(mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) &&
(mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE);
}
void XclExpFmlaCompImpl::RemoveTrailingParen()
{
// remove trailing tParen token
if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) )
mxData->maTokVec.pop_back();
// remove remaining tAttrSpace tokens
while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) )
mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() );
}
void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData )
{
mxData->maExtDataVec.push_back( nData );
}
void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount )
{
mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData );
}
void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData )
{
lclAppend( mxData->maExtDataVec, nData );
}
void XclExpFmlaCompImpl::AppendExt( sal_uInt32 nData )
{
lclAppend( mxData->maExtDataVec, nData );
}
void XclExpFmlaCompImpl::AppendExt( double fData )
{
lclAppend( mxData->maExtDataVec, fData );
}
void XclExpFmlaCompImpl::AppendExt( const String& rString )
{
lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? EXC_STR_DEFAULT : EXC_STR_8BITLENGTH );
}
// ============================================================================
namespace {
void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
{
if( b3DRefOnly )
{
// no reduction to 2D reference, if global link manager is used
rRef.SetFlag3D( sal_True );
}
else if( rScPos.Tab() == nCurrScTab )
{
rRef.SetTabRel( sal_True );
rRef.nRelTab = 0;
}
}
void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
{
ScSingleRefData aRef;
aRef.InitAddress( rScPos );
lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly );
rScTokArr.AddSingleReference( aRef );
}
void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly )
{
if( rScRange.aStart == rScRange.aEnd )
{
lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly );
}
else
{
ScComplexRefData aRef;
aRef.InitRange( rScRange );
lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly );
lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly );
rScTokArr.AddDoubleReference( aRef );
}
}
} // namespace
// ----------------------------------------------------------------------------
XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
mxImpl( new XclExpFmlaCompImpl( rRoot ) )
{
}
XclExpFormulaCompiler::~XclExpFormulaCompiler()
{
}
XclTokenArrayRef XclExpFormulaCompiler::CreateFormula(
XclFormulaType eType, const ScTokenArray& rScTokArr,
const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
{
return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos )
{
ScTokenArray aScTokArr;
lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
return mxImpl->CreateFormula( eType, aScTokArr );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange )
{
ScTokenArray aScTokArr;
lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
return mxImpl->CreateFormula( eType, aScTokArr );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges )
{
sal_uLong nCount = rScRanges.Count();
if( nCount == 0 )
return XclTokenArrayRef();
ScTokenArray aScTokArr;
SCTAB nCurrScTab = GetCurrScTab();
bool b3DRefOnly = mxImpl->Is3DRefOnly( eType );
for( sal_uLong nIdx = 0; nIdx < nCount; ++nIdx )
{
if( nIdx > 0 )
aScTokArr.AddOpCode( ocUnion );
lclPutRangeToTokenArray( aScTokArr, *rScRanges.GetObject( nIdx ), nCurrScTab, b3DRefOnly );
}
return mxImpl->CreateFormula( eType, aScTokArr );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode )
{
return mxImpl->CreateErrorFormula( nErrCode );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula(
sal_uInt8 nTokenId, const XclAddress& rXclPos )
{
return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos );
}
XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
sal_uInt16 nExtSheet, sal_uInt16 nExtName )
{
return mxImpl->CreateNameXFormula( nExtSheet, nExtName );
}
// ============================================================================