From 8ada1cd2846e5e60ad63250c68ddea3a9356546f Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 16 Mar 2012 12:28:31 +0000 Subject: move excel related oox bits to sc --- sc/source/filter/oox/formulaparser.cxx | 2930 ++++++++++++++++++++++++++++++++ 1 file changed, 2930 insertions(+) create mode 100644 sc/source/filter/oox/formulaparser.cxx (limited to 'sc/source/filter/oox/formulaparser.cxx') diff --git a/sc/source/filter/oox/formulaparser.cxx b/sc/source/filter/oox/formulaparser.cxx new file mode 100644 index 000000000000..eeb683f85ab0 --- /dev/null +++ b/sc/source/filter/oox/formulaparser.cxx @@ -0,0 +1,2930 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "formulaparser.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include "oox/core/filterbase.hxx" +#include "addressconverter.hxx" +#include "biffinputstream.hxx" +#include "defnamesbuffer.hxx" +#include "externallinkbuffer.hxx" +#include "tablebuffer.hxx" +#include "worksheethelper.hxx" + +namespace oox { +namespace xls { + +// ============================================================================ + +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::sheet::ReferenceFlags; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::uno; + +using ::rtl::OUString; + +// ============================================================================ + +namespace { + +sal_uInt16 lclReadFmlaSize( BiffInputStream& rStrm, BiffType eBiff, const sal_uInt16* pnFmlaSize ) +{ + return pnFmlaSize ? *pnFmlaSize : ((eBiff == BIFF2) ? rStrm.readuInt8() : rStrm.readuInt16()); +} + +} // namespace + +// formula finalizer ========================================================== + +FormulaFinalizer::FormulaFinalizer( const OpCodeProvider& rOpCodeProv ) : + OpCodeProvider( rOpCodeProv ), + ApiOpCodes( getOpCodes() ) +{ + maTokens.reserve( 0x2000 ); +} + +ApiTokenSequence FormulaFinalizer::finalizeTokenArray( const ApiTokenSequence& rTokens ) +{ + maTokens.clear(); + if( rTokens.hasElements() ) + { + const ApiToken* pToken = rTokens.getConstArray(); + processTokens( pToken, pToken + rTokens.getLength() ); + } + return ContainerHelper::vectorToSequence( maTokens ); +} + +const FunctionInfo* FormulaFinalizer::resolveBadFuncName( const OUString& ) const +{ + return 0; +} + +OUString FormulaFinalizer::resolveDefinedName( sal_Int32 ) const +{ + return OUString(); +} + +const FunctionInfo* FormulaFinalizer::getFunctionInfo( ApiToken& orFuncToken ) +{ + // first, try to find a regular function info from token op-code + if( const FunctionInfo* pRegFuncInfo = getFuncInfoFromApiToken( orFuncToken ) ) + return pRegFuncInfo; + + // try to recognize a function from an external library + if( (orFuncToken.OpCode == OPCODE_BAD) && orFuncToken.Data.has< OUString >() ) + { + // virtual call to resolveBadFuncName() + if( const FunctionInfo* pLibFuncInfo = resolveBadFuncName( orFuncToken.Data.get< OUString >() ) ) + { + // write function op-code to the OPCODE_BAD token + orFuncToken.OpCode = pLibFuncInfo->mnApiOpCode; + // if it is an external function, insert programmatic function name + if( (orFuncToken.OpCode == OPCODE_EXTERNAL) && (!pLibFuncInfo->maExtProgName.isEmpty()) ) + orFuncToken.Data <<= pLibFuncInfo->maExtProgName; + else + orFuncToken.Data.clear(); // clear string from OPCODE_BAD + return pLibFuncInfo; + } + } + + // no success - return null + return 0; +} + +const FunctionInfo* FormulaFinalizer::getExternCallInfo( ApiToken& orFuncToken, const ApiToken& rECToken ) +{ + // try to resolve the passed token to a supported sheet function + if( const FunctionInfo* pFuncInfo = getFuncInfoFromApiToken( rECToken ) ) + { + orFuncToken.OpCode = pFuncInfo->mnApiOpCode; + // programmatic add-in function name + if( (pFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && !pFuncInfo->maExtProgName.isEmpty() ) + orFuncToken.Data <<= pFuncInfo->maExtProgName; + // name of unsupported function, convert to OPCODE_BAD to preserve the name + else if( (pFuncInfo->mnApiOpCode == OPCODE_BAD) && !pFuncInfo->maOoxFuncName.isEmpty() ) + orFuncToken.Data <<= pFuncInfo->maOoxFuncName; + return pFuncInfo; + } + + // macro call or unknown function name, move data to function token + if( (rECToken.OpCode == OPCODE_MACRO) || (rECToken.OpCode == OPCODE_BAD) ) + orFuncToken = rECToken; + + // defined name used as function call, convert to OPCODE_BAD to preserve the name + if( (rECToken.OpCode == OPCODE_NAME) && rECToken.Data.has< sal_Int32 >() ) + { + OUString aDefName = resolveDefinedName( rECToken.Data.get< sal_Int32 >() ); + if( !aDefName.isEmpty() ) + { + orFuncToken.OpCode = OPCODE_BAD; + orFuncToken.Data <<= aDefName; + } + } + + return 0; +} + +void FormulaFinalizer::processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd ) +{ + while( pToken < pTokenEnd ) + { + // push the current token into the vector + bool bValid = appendFinalToken( *pToken ); + // try to process a function + if( const FunctionInfo* pFuncInfo = bValid ? getFunctionInfo( maTokens.back() ) : 0 ) + pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd ); + // otherwise, go to next token + else + ++pToken; + } +} + +const ApiToken* FormulaFinalizer::processParameters( + const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd ) +{ + // remember position of the token containing the function op-code + size_t nFuncNameIdx = maTokens.size() - 1; + + // process a function, if an OPCODE_OPEN token is following + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::processParameters - OPCODE_OPEN expected" ); + if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN) ) + { + // append the OPCODE_OPEN token to the vector + maTokens.append( OPCODE_OPEN ); + + // store positions of OPCODE_OPEN, parameter separators, and OPCODE_CLOSE + ParameterPosVector aParams; + pToken = findParameters( aParams, pToken, pTokenEnd ); + OSL_ENSURE( aParams.size() >= 2, "FormulaFinalizer::processParameters - missing tokens" ); + size_t nParamCount = aParams.size() - 1; + + if( (nParamCount == 1) && isEmptyParameter( aParams[ 0 ] + 1, aParams[ 1 ] ) ) + { + /* Empty pair of parentheses -> function call without parameters, + process parameter, there might be spaces between parentheses. */ + processTokens( aParams[ 0 ] + 1, aParams[ 1 ] ); + } + else + { + const FunctionInfo* pRealFuncInfo = &rFuncInfo; + ParameterPosVector::const_iterator aPosIt = aParams.begin(); + + /* Preprocess EXTERN.CALL functions. The actual function name is + contained as reference to a defined name in the first (hidden) + parameter. */ + if( rFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL ) + { + ApiToken& rFuncToken = maTokens[ nFuncNameIdx ]; + rFuncToken.OpCode = OPCODE_NONAME; + + // try to initialize function token from first parameter + if( const ApiToken* pECToken = getSingleToken( *aPosIt + 1, *(aPosIt + 1) ) ) + if( const FunctionInfo* pECFuncInfo = getExternCallInfo( rFuncToken, *pECToken ) ) + pRealFuncInfo = pECFuncInfo; + + /* On success (something has been inserted into rFuncToken), + skip the first parameter. */ + if( rFuncToken.OpCode != OPCODE_NONAME ) + { + --nParamCount; + ++aPosIt; + } + } + + // process all parameters + FunctionParamInfoIterator aParamInfoIt( *pRealFuncInfo ); + size_t nLastValidSize = maTokens.size(); + size_t nLastValidCount = 0; + for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aParamInfoIt ) + { + // add embedded Calc-only parameters + if( aParamInfoIt.isCalcOnlyParam() ) + { + appendCalcOnlyParameter( *pRealFuncInfo, nParam ); + while( aParamInfoIt.isCalcOnlyParam() ) ++aParamInfoIt; + } + + const ApiToken* pParamBegin = *aPosIt + 1; + const ApiToken* pParamEnd = *(aPosIt + 1); + bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd ); + + if( !aParamInfoIt.isExcelOnlyParam() ) + { + // handle empty parameters + if( bIsEmpty ) + { + // append leading space tokens from original token array + while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) ) + maTokens.push_back( *pParamBegin++ ); + // add default values for some empty parameters, or the OPCODE_MISSING token + appendEmptyParameter( *pRealFuncInfo, nParam ); + // reset bIsEmpty flag, if something has been appended in appendEmptyParameter() + bIsEmpty = maTokens.back().OpCode == OPCODE_MISSING; + // skip OPCODE_MISSING token in the original token array + OSL_ENSURE( (pParamBegin == pParamEnd) || (pParamBegin->OpCode == OPCODE_MISSING), "FormulaFinalizer::processParameters - OPCODE_MISSING expected" ); + if( pParamBegin < pParamEnd ) ++pParamBegin; + // append trailing space tokens from original token array + while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) ) + maTokens.push_back( *pParamBegin++ ); + } + else + { + // if parameter is not empty, process all tokens of the parameter + processTokens( pParamBegin, pParamEnd ); + } + + // append parameter separator token + maTokens.append( OPCODE_SEP ); + } + + /* #84453# Update size of new token sequence with valid parameters + to be able to remove trailing optional empty parameters. */ + if( !bIsEmpty || (nParam < pRealFuncInfo->mnMinParamCount) ) + { + nLastValidSize = maTokens.size(); + nLastValidCount = nParam + 1; + } + } + + // #84453# remove trailing optional empty parameters + maTokens.resize( nLastValidSize ); + + // add trailing Calc-only parameters + if( aParamInfoIt.isCalcOnlyParam() ) + appendCalcOnlyParameter( *pRealFuncInfo, nLastValidCount ); + + // add optional parameters that are required in Calc + appendRequiredParameters( *pRealFuncInfo, nLastValidCount ); + + // remove last parameter separator token + if( maTokens.back().OpCode == OPCODE_SEP ) + maTokens.pop_back(); + } + + /* Append the OPCODE_CLOSE token to the vector, but only if there is + no OPCODE_BAD token at the end, this token already contains the + trailing closing parentheses. */ + if( (pTokenEnd - 1)->OpCode != OPCODE_BAD ) + maTokens.append( OPCODE_CLOSE ); + } + + /* Replace OPCODE_EXTERNAL with OPCODE_NONAME to get #NAME! error in cell, + if no matching add-in function was found. */ + ApiToken& rFuncNameToken = maTokens[ nFuncNameIdx ]; + if( (rFuncNameToken.OpCode == OPCODE_EXTERNAL) && !rFuncNameToken.Data.hasValue() ) + rFuncNameToken.OpCode = OPCODE_NONAME; + + return pToken; +} + +bool FormulaFinalizer::isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; + if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_MISSING) ) ++pToken; + while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; + return pToken == pTokenEnd; +} + +const ApiToken* FormulaFinalizer::getSingleToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + const ApiToken* pSingleToken = 0; + // skip leading whitespace tokens + while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; + // remember first non-whitespace token + if( pToken < pTokenEnd ) pSingleToken = pToken++; + // skip trailing whitespace tokens + while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; + // return null, if other non-whitespace tokens follow + return (pToken == pTokenEnd) ? pSingleToken : 0; +} + +const ApiToken* FormulaFinalizer::skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + // skip tokens between OPCODE_OPEN and OPCODE_CLOSE + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "skipParentheses - OPCODE_OPEN expected" ); + ++pToken; + while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) ) + { + if( pToken->OpCode == OPCODE_OPEN ) + pToken = skipParentheses( pToken, pTokenEnd ); + else + ++pToken; + } + // skip the OPCODE_CLOSE token + OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "skipParentheses - OPCODE_CLOSE expected" ); + return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd; +} + +const ApiToken* FormulaFinalizer::findParameters( ParameterPosVector& rParams, + const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + // push position of OPCODE_OPEN + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::findParameters - OPCODE_OPEN expected" ); + rParams.push_back( pToken++ ); + + // find positions of parameter separators + while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) ) + { + if( pToken->OpCode == OPCODE_OPEN ) + pToken = skipParentheses( pToken, pTokenEnd ); + else if( pToken->OpCode == OPCODE_SEP ) + rParams.push_back( pToken++ ); + else + ++pToken; + } + + // push position of OPCODE_CLOSE + OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "FormulaFinalizer::findParameters - OPCODE_CLOSE expected" ); + rParams.push_back( pToken ); + return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd; +} + +void FormulaFinalizer::appendEmptyParameter( const FunctionInfo& rFuncInfo, size_t nParam ) +{ + // remember old size of the token array + size_t nTokenArraySize = maTokens.size(); + + switch( rFuncInfo.mnBiff12FuncId ) + { + case BIFF_FUNC_IF: + if( (nParam == 1) || (nParam == 2) ) + maTokens.append< double >( OPCODE_PUSH, 0.0 ); + break; + default:; + } + + // if no token has been added, append a OPCODE_MISSING token + if( nTokenArraySize == maTokens.size() ) + maTokens.append( OPCODE_MISSING ); +} + +void FormulaFinalizer::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ) +{ + (void)nParam; // prevent 'unused' warning + switch( rFuncInfo.mnBiff12FuncId ) + { + case BIFF_FUNC_FLOOR: + case BIFF_FUNC_CEILING: + OSL_ENSURE( nParam == 2, "FormulaFinalizer::appendCalcOnlyParameter - unexpected parameter index" ); + maTokens.append< double >( OPCODE_PUSH, 1.0 ); + maTokens.append( OPCODE_SEP ); + break; + } +} + +void FormulaFinalizer::appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount ) +{ + switch( rFuncInfo.mnBiff12FuncId ) + { + case BIFF_FUNC_WEEKNUM: + if( nParamCount == 1 ) + { + maTokens.append< double >( OPCODE_PUSH, 1.0 ); + maTokens.append( OPCODE_SEP ); + } + break; + } +} + +bool FormulaFinalizer::appendFinalToken( const ApiToken& rToken ) +{ + // replace OPCODE_MACRO without macro name with #NAME? error code + bool bValid = (rToken.OpCode != OPCODE_MACRO) || rToken.Data.hasValue(); + if( bValid ) + { + maTokens.push_back( rToken ); + } + else + { + maTokens.append( OPCODE_ARRAY_OPEN ); + maTokens.append( OPCODE_PUSH, BiffHelper::calcDoubleFromError( BIFF_ERR_NAME ) ); + maTokens.append( OPCODE_ARRAY_CLOSE ); + } + return bValid; +} + +// parser implementation base ================================================= + +class FormulaParserImpl : public FormulaFinalizer, public WorkbookHelper +{ +public: + explicit FormulaParserImpl( const FormulaParser& rParent ); + + /** Converts an OOXML formula string. */ + virtual ApiTokenSequence importOoxFormula( + const CellAddress& rBaseAddress, + const OUString& rFormulaString ); + + /** Imports and converts a BIFF12 token array from the passed stream. */ + virtual ApiTokenSequence importBiff12Formula( + const CellAddress& rBaseAddress, + FormulaType eType, + SequenceInputStream& rStrm ); + + /** Imports and converts a BIFF2-BIFF8 token array from the passed stream. */ + virtual ApiTokenSequence importBiffFormula( + const CellAddress& rBaseAddress, + FormulaType eType, + BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ); + + /** Tries to resolve the passed ref-id to an OLE target URL. */ + OUString resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const; + +protected: + typedef ::std::pair< sal_Int32, bool > WhiteSpace; + typedef ::std::vector< WhiteSpace > WhiteSpaceVec; + + /** Initializes the formula parser before importing a formula. */ + void initializeImport( const CellAddress& rBaseAddress, FormulaType eType ); + /** Finalizes the internal token storage after import. */ + ApiTokenSequence finalizeImport(); + + // token array ------------------------------------------------------------ + + bool resetSpaces(); + static void appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed ); + void appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed ); + void appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed ); + void appendClosingSpaces( sal_Int32 nCount, bool bLineFeed ); + + size_t getFormulaSize() const; + Any& appendRawToken( sal_Int32 nOpCode ); + Any& insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd ); + size_t appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces ); + size_t insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd ); + + size_t getOperandSize( size_t nOpCountFromEnd, size_t nOpIndex ) const; + void pushOperandSize( size_t nSize ); + size_t popOperandSize(); + + ApiToken& getOperandToken( size_t nOpCountFromEnd, size_t nOpIndex, size_t nTokenIndex ); + void removeOperand( size_t nOpCountFromEnd, size_t nOpIndex ); + + bool pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + bool pushAnyOperandToken( const Any& rAny, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + template< typename Type > + bool pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + template< typename Type > + inline bool pushValueOperandToken( const Type& rValue, const WhiteSpaceVec* pSpaces = 0 ) + { return pushValueOperandToken( rValue, OPCODE_PUSH, pSpaces ); } + bool pushParenthesesOperandToken( const WhiteSpaceVec* pOpeningSpaces = 0, const WhiteSpaceVec* pClosingSpaces = 0 ); + bool pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + bool pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + bool pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = 0 ); + bool pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces = 0, const WhiteSpaceVec* pClosingSpaces = 0 ); + bool pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = 0, const WhiteSpaceVec* pClosingSpaces = 0 ); + bool pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = 0, const WhiteSpaceVec* pClosingSpaces = 0 ); + + bool pushOperand( sal_Int32 nOpCode ); + bool pushAnyOperand( const Any& rAny, sal_Int32 nOpCode ); + template< typename Type > + bool pushValueOperand( const Type& rValue, sal_Int32 nOpCode ); + template< typename Type > + inline bool pushValueOperand( const Type& rValue ) + { return pushValueOperand( rValue, OPCODE_PUSH ); } + bool pushBoolOperand( bool bValue ); + bool pushErrorOperand( double fEncodedError ); + bool pushBiffBoolOperand( sal_uInt8 nValue ); + bool pushBiffErrorOperand( sal_uInt8 nErrorCode ); + bool pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + template< typename Type > + bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef ); + bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushNlrOperand( const BinSingleRef2d& rRef ); + bool pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken ); + bool pushDefinedNameOperand( const DefinedNameRef& rxDefName ); + bool pushExternalFuncOperand( const FunctionInfo& rFuncInfo ); + bool pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem ); + bool pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink ); + bool pushSpecialTokenOperand( const BinAddress& rBaseAddr, bool bTable ); + + bool pushUnaryPreOperator( sal_Int32 nOpCode ); + bool pushUnaryPostOperator( sal_Int32 nOpCode ); + bool pushBinaryOperator( sal_Int32 nOpCode ); + bool pushParenthesesOperator(); + bool pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount ); + bool pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount ); + +private: + // reference conversion --------------------------------------------------- + + void initReference2d( SingleReference& orApiRef ) const; + void initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet ) const; + void convertColRow( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bRelativeAsOffset ) const; + void convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + +private: + // finalize token sequence ------------------------------------------------ + + virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const; + virtual ::rtl::OUString resolveDefinedName( sal_Int32 nTokenIndex ) const; + +protected: + const sal_Int32 mnMaxApiCol; /// Maximum column index in own document. + const sal_Int32 mnMaxApiRow; /// Maximum row index in own document. + const sal_Int32 mnMaxXlsCol; /// Maximum column index in imported document. + const sal_Int32 mnMaxXlsRow; /// Maximum row index in imported document. + + CellAddress maBaseAddr; /// Base address for relative references. + bool mbRelativeAsOffset; /// True = relative row/column index is (signed) offset, false = explicit index. + bool mb2dRefsAs3dRefs; /// True = convert all 2D references to 3D references in sheet specified by base address. + bool mbSpecialTokens; /// True = special handling for tExp and tTbl tokens, false = exit with error. + bool mbAllowNulChars; /// True = keep NUL characters in string tokens. + +private: + typedef ::std::vector< size_t > SizeTypeVector; + + ApiTokenVector maTokenStorage; /// Raw unordered token storage. + SizeTypeVector maTokenIndexes; /// Indexes into maTokenStorage. + SizeTypeVector maOperandSizeStack; /// Stack with token sizes per operand. + WhiteSpaceVec maLeadingSpaces; /// List of whitespaces before next token. + WhiteSpaceVec maOpeningSpaces; /// List of whitespaces before opening parenthesis. + WhiteSpaceVec maClosingSpaces; /// List of whitespaces before closing parenthesis. +}; + +// ---------------------------------------------------------------------------- + +FormulaParserImpl::FormulaParserImpl( const FormulaParser& rParent ) : + FormulaFinalizer( rParent ), + WorkbookHelper( rParent ), + mnMaxApiCol( rParent.getAddressConverter().getMaxApiAddress().Column ), + mnMaxApiRow( rParent.getAddressConverter().getMaxApiAddress().Row ), + mnMaxXlsCol( rParent.getAddressConverter().getMaxXlsAddress().Column ), + mnMaxXlsRow( rParent.getAddressConverter().getMaxXlsAddress().Row ), + mbRelativeAsOffset( false ), + mb2dRefsAs3dRefs( false ), + mbSpecialTokens( false ), + mbAllowNulChars( false ) +{ + // reserve enough space to make resize(), push_back() etc. cheap + maTokenStorage.reserve( 0x2000 ); + maTokenIndexes.reserve( 0x2000 ); + maOperandSizeStack.reserve( 256 ); + maLeadingSpaces.reserve( 256 ); + maOpeningSpaces.reserve( 256 ); + maClosingSpaces.reserve( 256 ); +} + +ApiTokenSequence FormulaParserImpl::importOoxFormula( const CellAddress&, const OUString& ) +{ + OSL_FAIL( "FormulaParserImpl::importOoxFormula - not implemented" ); + return ApiTokenSequence(); +} + +ApiTokenSequence FormulaParserImpl::importBiff12Formula( const CellAddress&, FormulaType, SequenceInputStream& ) +{ + OSL_FAIL( "FormulaParserImpl::importBiff12Formula - not implemented" ); + return ApiTokenSequence(); +} + +ApiTokenSequence FormulaParserImpl::importBiffFormula( const CellAddress&, FormulaType, BiffInputStream&, const sal_uInt16* ) +{ + OSL_FAIL( "FormulaParserImpl::importBiffFormula - not implemented" ); + return ApiTokenSequence(); +} + +OUString FormulaParserImpl::resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const +{ + const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, bUseRefSheets ).get(); + OSL_ENSURE( pExtLink && (pExtLink->getLinkType() == LINKTYPE_OLE), "FormulaParserImpl::resolveOleTarget - missing or wrong link" ); + if( pExtLink && (pExtLink->getLinkType() == LINKTYPE_OLE) ) + return getBaseFilter().getAbsoluteUrl( pExtLink->getTargetUrl() ); + return OUString(); +} + +void FormulaParserImpl::initializeImport( const CellAddress& rBaseAddr, FormulaType eType ) +{ + maBaseAddr = rBaseAddr; + mbRelativeAsOffset = mb2dRefsAs3dRefs = mbSpecialTokens = mbAllowNulChars = false; + switch( eType ) + { + case FORMULATYPE_CELL: + mbSpecialTokens = true; + break; + case FORMULATYPE_ARRAY: + break; + case FORMULATYPE_SHAREDFORMULA: + mbRelativeAsOffset = true; + break; + case FORMULATYPE_CONDFORMAT: + mbRelativeAsOffset = true; + break; + case FORMULATYPE_VALIDATION: + mbRelativeAsOffset = true; + // enable NUL characters in BIFF import, string list is single tStr token with NUL separators + mbAllowNulChars = getFilterType() == FILTER_BIFF; + break; + case FORMULATYPE_DEFINEDNAME: + mbRelativeAsOffset = true; + // BIFF2-BIFF4: convert 2D referebces to absolute 3D references + mb2dRefsAs3dRefs = (getFilterType() == FILTER_BIFF) && (getBiff() <= BIFF4); + break; + } + + maTokenStorage.clear(); + maTokenIndexes.clear(); + maOperandSizeStack.clear(); +} + +ApiTokenSequence FormulaParserImpl::finalizeImport() +{ + ApiTokenSequence aTokens( static_cast< sal_Int32 >( maTokenIndexes.size() ) ); + if( aTokens.hasElements() ) + { + ApiToken* pToken = aTokens.getArray(); + for( SizeTypeVector::const_iterator aIt = maTokenIndexes.begin(), aEnd = maTokenIndexes.end(); aIt != aEnd; ++aIt, ++pToken ) + *pToken = maTokenStorage[ *aIt ]; + } + return finalizeTokenArray( aTokens ); +} + +// token array ---------------------------------------------------------------- + +bool FormulaParserImpl::resetSpaces() +{ + maLeadingSpaces.clear(); + maOpeningSpaces.clear(); + maClosingSpaces.clear(); + return true; +} + +void FormulaParserImpl::appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed ) +{ + OSL_ENSURE( nCount >= 0, "FormulaParserImpl::appendSpaces - negative count" ); + if( nCount > 0 ) + orSpaces.push_back( WhiteSpace( nCount, bLineFeed ) ); +} + +void FormulaParserImpl::appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed ) +{ + appendSpaces( maLeadingSpaces, nCount, bLineFeed ); +} + +void FormulaParserImpl::appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed ) +{ + appendSpaces( maOpeningSpaces, nCount, bLineFeed ); +} + +void FormulaParserImpl::appendClosingSpaces( sal_Int32 nCount, bool bLineFeed ) +{ + appendSpaces( maClosingSpaces, nCount, bLineFeed ); +} + +size_t FormulaParserImpl::getFormulaSize() const +{ + return maTokenIndexes.size(); +} + +Any& FormulaParserImpl::appendRawToken( sal_Int32 nOpCode ) +{ + maTokenIndexes.push_back( maTokenStorage.size() ); + return maTokenStorage.append( nOpCode ); +} + +Any& FormulaParserImpl::insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd ) +{ + maTokenIndexes.insert( maTokenIndexes.end() - nIndexFromEnd, maTokenStorage.size() ); + return maTokenStorage.append( nOpCode ); +} + +size_t FormulaParserImpl::appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces ) +{ + if( pSpaces && !pSpaces->empty() ) + for( WhiteSpaceVec::const_iterator aIt = pSpaces->begin(), aEnd = pSpaces->end(); aIt != aEnd; ++aIt ) + appendRawToken( OPCODE_SPACES ) <<= aIt->first; + return pSpaces ? pSpaces->size() : 0; +} + +size_t FormulaParserImpl::insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd ) +{ + if( pSpaces && !pSpaces->empty() ) + for( WhiteSpaceVec::const_iterator aIt = pSpaces->begin(), aEnd = pSpaces->end(); aIt != aEnd; ++aIt ) + insertRawToken( OPCODE_SPACES, nIndexFromEnd ) <<= aIt->first; + return pSpaces ? pSpaces->size() : 0; +} + +size_t FormulaParserImpl::getOperandSize( size_t nOpCountFromEnd, size_t nOpIndex ) const +{ + OSL_ENSURE( (nOpIndex < nOpCountFromEnd) && (nOpCountFromEnd <= maOperandSizeStack.size()), + "FormulaParserImpl::getOperandSize - invalid parameters" ); + return maOperandSizeStack[ maOperandSizeStack.size() - nOpCountFromEnd + nOpIndex ]; +} + +void FormulaParserImpl::pushOperandSize( size_t nSize ) +{ + maOperandSizeStack.push_back( nSize ); +} + +size_t FormulaParserImpl::popOperandSize() +{ + OSL_ENSURE( !maOperandSizeStack.empty(), "FormulaParserImpl::popOperandSize - invalid call" ); + size_t nOpSize = maOperandSizeStack.back(); + maOperandSizeStack.pop_back(); + return nOpSize; +} + +ApiToken& FormulaParserImpl::getOperandToken( size_t nOpCountFromEnd, size_t nOpIndex, size_t nTokenIndex ) +{ + OSL_ENSURE( getOperandSize( nOpCountFromEnd, nOpIndex ) > nTokenIndex, + "FormulaParserImpl::getOperandToken - invalid parameters" ); + SizeTypeVector::const_iterator aIndexIt = maTokenIndexes.end(); + for( SizeTypeVector::const_iterator aEnd = maOperandSizeStack.end(), aIt = aEnd - nOpCountFromEnd + nOpIndex; aIt != aEnd; ++aIt ) + aIndexIt -= *aIt; + return maTokenStorage[ *(aIndexIt + nTokenIndex) ]; +} + +bool FormulaParserImpl::pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces ); + appendRawToken( nOpCode ); + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +bool FormulaParserImpl::pushAnyOperandToken( const Any& rAny, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces ); + appendRawToken( nOpCode ) = rAny; + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +template< typename Type > +bool FormulaParserImpl::pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces ); + appendRawToken( nOpCode ) <<= rValue; + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +bool FormulaParserImpl::pushParenthesesOperandToken( const WhiteSpaceVec* pOpeningSpaces, const WhiteSpaceVec* pClosingSpaces ) +{ + size_t nSpacesSize = appendWhiteSpaceTokens( pOpeningSpaces ); + appendRawToken( OPCODE_OPEN ); + nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces ); + appendRawToken( OPCODE_CLOSE ); + pushOperandSize( nSpacesSize + 2 ); + return true; +} + +bool FormulaParserImpl::pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOpSize ); + insertRawToken( nOpCode, nOpSize ); + pushOperandSize( nOpSize + nSpacesSize + 1 ); + } + return bOk; +} + +bool FormulaParserImpl::pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces ); + appendRawToken( nOpCode ); + pushOperandSize( nOpSize + nSpacesSize + 1 ); + } + return bOk; +} + +bool FormulaParserImpl::pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 2; + if( bOk ) + { + size_t nOp2Size = popOperandSize(); + size_t nOp1Size = popOperandSize(); + size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOp2Size ); + insertRawToken( nOpCode, nOp2Size ); + pushOperandSize( nOp1Size + nSpacesSize + 1 + nOp2Size ); + } + return bOk; +} + +bool FormulaParserImpl::pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces, const WhiteSpaceVec* pClosingSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = insertWhiteSpaceTokens( pOpeningSpaces, nOpSize ); + insertRawToken( OPCODE_OPEN, nOpSize ); + nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces ); + appendRawToken( OPCODE_CLOSE ); + pushOperandSize( nOpSize + nSpacesSize + 2 ); + } + return bOk; +} + +bool FormulaParserImpl::pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces ) +{ + /* #i70925# if there are not enough tokens available on token stack, do + not exit with error, but reduce parameter count. */ + nParamCount = ::std::min( maOperandSizeStack.size(), nParamCount ); + + // convert all parameters on stack to a single operand separated with OPCODE_SEP + bool bOk = true; + for( size_t nParam = 1; bOk && (nParam < nParamCount); ++nParam ) + bOk = pushBinaryOperatorToken( OPCODE_SEP ); + + // add function parentheses and function name + return bOk && + ((nParamCount > 0) ? pushParenthesesOperatorToken( 0, pClosingSpaces ) : pushParenthesesOperandToken( 0, pClosingSpaces )) && + pushUnaryPreOperatorToken( nOpCode, pLeadingSpaces ); +} + +bool FormulaParserImpl::pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces ) +{ + bool bOk = pushFunctionOperatorToken( rFuncInfo.mnApiOpCode, nParamCount, pLeadingSpaces, pClosingSpaces ); + if( bOk ) + { + // create an external add-in call for the passed built-in function + if( (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) && !rFuncInfo.maExtProgName.isEmpty() ) + getOperandToken( 1, 0, 0 ).Data <<= rFuncInfo.maExtProgName; + // create a bad token with unsupported function name + else if( (rFuncInfo.mnApiOpCode == OPCODE_BAD) && !rFuncInfo.maOoxFuncName.isEmpty() ) + getOperandToken( 1, 0, 0 ).Data <<= rFuncInfo.maOoxFuncName; + } + return bOk; +} + +bool FormulaParserImpl::pushOperand( sal_Int32 nOpCode ) +{ + return pushOperandToken( nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushAnyOperand( const Any& rAny, sal_Int32 nOpCode ) +{ + return pushAnyOperandToken( rAny, nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +template< typename Type > +bool FormulaParserImpl::pushValueOperand( const Type& rValue, sal_Int32 nOpCode ) +{ + return pushValueOperandToken( rValue, nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushBoolOperand( bool bValue ) +{ + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( bValue ? BIFF_FUNC_TRUE : BIFF_FUNC_FALSE ) ) + return pushFunctionOperator( pFuncInfo->mnApiOpCode, 0 ); + return pushValueOperand< double >( bValue ? 1.0 : 0.0 ); +} + +bool FormulaParserImpl::pushErrorOperand( double fEncodedError ) +{ + // HACK: enclose all error codes into an 1x1 matrix + // start token array with opening brace and leading spaces + pushOperand( OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = maTokenIndexes.size(); + // push a double containing the Calc error code + appendRawToken( OPCODE_PUSH ) <<= fEncodedError; + // close token array and set resulting operand size + appendRawToken( OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + maTokenIndexes.size() - nOldArraySize ); + return true; +} + +bool FormulaParserImpl::pushBiffBoolOperand( sal_uInt8 nValue ) +{ + return pushBoolOperand( nValue != BIFF_TOK_BOOL_FALSE ); +} + +bool FormulaParserImpl::pushBiffErrorOperand( sal_uInt8 nErrorCode ) +{ + return pushErrorOperand( BiffHelper::calcDoubleFromError( nErrorCode ) ); +} + +bool FormulaParserImpl::pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + SingleReference aApiRef; + convertReference2d( aApiRef, rRef, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + ComplexReference aApiRef; + convertReference2d( aApiRef, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +template< typename Type > +bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef ) +{ + if( rSheetRange.isExternal() ) + { + ExternalReference aApiExtRef; + aApiExtRef.Index = rSheetRange.getDocLinkIndex(); + aApiExtRef.Reference <<= rApiRef; + return pushValueOperand( aApiExtRef ); + } + return pushValueOperand( rApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + if( rSheetRange.is3dRange() ) + { + // single-cell-range over several sheets, needs to create a ComplexReference + ComplexReference aApiRef; + convertReference3d( aApiRef, rSheetRange, rRef, rRef, bDeleted, bRelativeAsOffset ); + return pushReferenceOperand( rSheetRange, aApiRef ); + } + SingleReference aApiRef; + convertReference3d( aApiRef, rSheetRange.getFirstSheet(), rSheetRange.isSameSheet(), rRef, bDeleted, bRelativeAsOffset ); + return pushReferenceOperand( rSheetRange, aApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + ComplexReference aApiRef; + convertReference3d( aApiRef, rSheetRange, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset ); + return pushReferenceOperand( rSheetRange, aApiRef ); +} + +bool FormulaParserImpl::pushNlrOperand( const BinSingleRef2d& rRef ) +{ + SingleReference aApiRef; + convertReference2d( aApiRef, rRef, false, false ); + return pushValueOperand( aApiRef, OPCODE_NLR ); +} + +bool FormulaParserImpl::pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken ) +{ + Any aRefAny = rName.getReference( maBaseAddr ); + if( aRefAny.hasValue() ) + return pushAnyOperand( aRefAny, OPCODE_PUSH ); + if( bPushBadToken && !rName.getModelName().isEmpty() && (rName.getModelName()[ 0 ] >= ' ') ) + return pushValueOperand( rName.getModelName(), OPCODE_BAD ); + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool FormulaParserImpl::pushDefinedNameOperand( const DefinedNameRef& rxDefName ) +{ + if( !rxDefName || rxDefName->getModelName().isEmpty() ) + return pushBiffErrorOperand( BIFF_ERR_NAME ); + if( rxDefName->isMacroFunction() ) + return pushValueOperand( rxDefName->getModelName(), OPCODE_MACRO ); + if( rxDefName->getTokenIndex() >= 0 ) + return pushValueOperand( rxDefName->getTokenIndex(), OPCODE_NAME ); + return pushEmbeddedRefOperand( *rxDefName, true ); +} + +bool FormulaParserImpl::pushExternalFuncOperand( const FunctionInfo& rFuncInfo ) +{ + return (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) ? + pushValueOperand( rFuncInfo.maExtProgName, OPCODE_EXTERNAL ) : + pushOperand( rFuncInfo.mnApiOpCode ); +} + +bool FormulaParserImpl::pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem ) +{ + // create the function call DDE("server";"topic";"item") + return + pushValueOperandToken( rDdeServer ) && + pushValueOperandToken( rDdeTopic ) && + pushValueOperandToken( rDdeItem ) && + pushFunctionOperator( OPCODE_DDE, 3 ); +} + +bool FormulaParserImpl::pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink ) +{ + if( rxExtName.get() ) switch( rExtLink.getLinkType() ) + { + case LINKTYPE_INTERNAL: + case LINKTYPE_EXTERNAL: + return pushEmbeddedRefOperand( *rxExtName, false ); + + case LINKTYPE_ANALYSIS: + // TODO: need support for localized addin function names + if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( rxExtName->getUpcaseModelName() ) ) + return pushExternalFuncOperand( *pFuncInfo ); + break; + + case LINKTYPE_LIBRARY: + if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( rxExtName->getUpcaseModelName() ) ) + if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == rExtLink.getFuncLibraryType()) ) + return pushExternalFuncOperand( *pFuncInfo ); + break; + + case LINKTYPE_DDE: + { + OUString aDdeServer, aDdeTopic, aDdeItem; + if( rxExtName->getDdeLinkData( aDdeServer, aDdeTopic, aDdeItem ) ) + return pushDdeLinkOperand( aDdeServer, aDdeTopic, aDdeItem ); + } + break; + + default: + OSL_ENSURE( rExtLink.getLinkType() != LINKTYPE_SELF, "FormulaParserImpl::pushExternalNameOperand - invalid call" ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool FormulaParserImpl::pushSpecialTokenOperand( const BinAddress& rBaseAddr, bool bTable ) +{ + CellAddress aBaseAddr( maBaseAddr.Sheet, rBaseAddr.mnCol, rBaseAddr.mnRow ); + ApiSpecialTokenInfo aTokenInfo( aBaseAddr, bTable ); + return mbSpecialTokens && (getFormulaSize() == 0) && pushValueOperand( aTokenInfo, OPCODE_BAD ); +} + +bool FormulaParserImpl::pushUnaryPreOperator( sal_Int32 nOpCode ) +{ + return pushUnaryPreOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushUnaryPostOperator( sal_Int32 nOpCode ) +{ + return pushUnaryPostOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushBinaryOperator( sal_Int32 nOpCode ) +{ + return pushBinaryOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushParenthesesOperator() +{ + return pushParenthesesOperatorToken( &maOpeningSpaces, &maClosingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount ) +{ + return pushFunctionOperatorToken( nOpCode, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount ) +{ + return pushFunctionOperatorToken( rFuncInfo, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces(); +} + +// reference conversion ------------------------------------------------------- + +void FormulaParserImpl::initReference2d( SingleReference& orApiRef ) const +{ + if( mb2dRefsAs3dRefs ) + { + initReference3d( orApiRef, maBaseAddr.Sheet, false ); + } + else + { + orApiRef.Flags = SHEET_RELATIVE; + // #i10184# absolute sheet index needed for relative references in shared formulas + orApiRef.Sheet = maBaseAddr.Sheet; + orApiRef.RelativeSheet = 0; + } +} + +void FormulaParserImpl::initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet ) const +{ + orApiRef.Flags = SHEET_3D; + if( nSheet < 0 ) + { + orApiRef.Sheet = 0; + orApiRef.Flags |= SHEET_DELETED; + } + else if( bSameSheet ) + { + OSL_ENSURE( nSheet == 0, "FormulaParserImpl::initReference3d - invalid sheet index" ); + orApiRef.Flags |= SHEET_RELATIVE; + orApiRef.RelativeSheet = 0; + } + else + { + orApiRef.Sheet = nSheet; + } +} + +void FormulaParserImpl::convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + if( bDeleted ) + { + orApiRef.Column = 0; + orApiRef.Row = 0; + // no explicit information about whether row or column is deleted + orApiRef.Flags |= COLUMN_DELETED | ROW_DELETED; + } + else + { + // column/row indexes and flags + setFlag( orApiRef.Flags, COLUMN_RELATIVE, rRef.mbColRel ); + setFlag( orApiRef.Flags, ROW_RELATIVE, rRef.mbRowRel ); + (rRef.mbColRel ? orApiRef.RelativeColumn : orApiRef.Column) = rRef.mnCol; + (rRef.mbRowRel ? orApiRef.RelativeRow : orApiRef.Row) = rRef.mnRow; + // convert absolute indexes to relative offsets used in API + if( !bRelativeAsOffset ) + { + if( rRef.mbColRel ) + orApiRef.RelativeColumn -= maBaseAddr.Column; + if( rRef.mbRowRel ) + orApiRef.RelativeRow -= maBaseAddr.Row; + } + } +} + +void FormulaParserImpl::convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + convertReference( orApiRef.Reference1, rRef1, bDeleted, bRelativeAsOffset ); + convertReference( orApiRef.Reference2, rRef2, bDeleted, bRelativeAsOffset ); + /* Handle references to complete rows or columns (e.g. $1:$2 or C:D), + need to expand or shrink to limits of own document. */ + if( !bDeleted && !rRef1.mbColRel && !rRef2.mbColRel && (orApiRef.Reference1.Column == 0) && (orApiRef.Reference2.Column == mnMaxXlsCol) ) + orApiRef.Reference2.Column = mnMaxApiCol; + if( !bDeleted && !rRef1.mbRowRel && !rRef2.mbRowRel && (orApiRef.Reference1.Row == 0) && (orApiRef.Reference2.Row == mnMaxXlsRow) ) + orApiRef.Reference2.Row = mnMaxApiRow; +} + +void FormulaParserImpl::convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference2d( orApiRef ); + convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset ); +} + +void FormulaParserImpl::convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference2d( orApiRef.Reference1 ); + initReference2d( orApiRef.Reference2 ); + convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset ); + // remove sheet name from second part of reference + setFlag( orApiRef.Reference2.Flags, SHEET_3D, false ); +} + +void FormulaParserImpl::convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference3d( orApiRef, nSheet, bSameSheet ); + convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset ); +} + +void FormulaParserImpl::convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + bool bSameSheet = rSheetRange.isSameSheet(); + initReference3d( orApiRef.Reference1, rSheetRange.getFirstSheet(), bSameSheet ); + initReference3d( orApiRef.Reference2, rSheetRange.getLastSheet(), bSameSheet ); + convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset ); + // remove sheet name from second part of reference + setFlag( orApiRef.Reference2.Flags, SHEET_3D, rSheetRange.is3dRange() ); +} + +// finalize token sequence ---------------------------------------------------- + +const FunctionInfo* FormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const +{ + /* Try to parse calls to library functions. The format of such a function + call is "[n]!funcname", n>0 being the link identifier of the function + library spreadsheet file. */ + sal_Int32 nBracketOpen = rTokenData.indexOf( '[' ); + sal_Int32 nBracketClose = rTokenData.indexOf( ']' ); + sal_Int32 nExclamation = rTokenData.indexOf( '!' ); + if( (0 == nBracketOpen) && (nBracketOpen + 1 < nBracketClose) && (nBracketClose + 1 == nExclamation) && (nExclamation + 1 < rTokenData.getLength()) ) + { + sal_Int32 nRefId = rTokenData.copy( nBracketOpen + 1, nBracketClose - nBracketOpen - 1 ).toInt32(); + const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get(); + if( pExtLink && (pExtLink->getLinkType() == LINKTYPE_LIBRARY) ) + { + OUString aFuncName = rTokenData.copy( nExclamation + 1 ).toAsciiUpperCase(); + if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName ) ) + if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == pExtLink->getFuncLibraryType()) ) + return pFuncInfo; + } + } + return 0; +} + +OUString FormulaParserImpl::resolveDefinedName( sal_Int32 nTokenIndex ) const +{ + if( const DefinedName* pDefName = getDefinedNames().getByTokenIndex( nTokenIndex ).get() ) + return pDefName->getCalcName(); + return OUString(); +} + +// OOXML/BIFF12 parser implementation ========================================= + +class OoxFormulaParserImpl : public FormulaParserImpl +{ +public: + explicit OoxFormulaParserImpl( const FormulaParser& rParent ); + + virtual ApiTokenSequence importOoxFormula( + const CellAddress& rBaseAddr, + const OUString& rFormulaString ); + + virtual ApiTokenSequence importBiff12Formula( + const CellAddress& rBaseAddr, + FormulaType eType, + SequenceInputStream& rStrm ); + +private: + // import token contents and create API formula token --------------------- + + bool importAttrToken( SequenceInputStream& rStrm ); + bool importSpaceToken( SequenceInputStream& rStrm ); + bool importTableToken( SequenceInputStream& rStrm ); + bool importArrayToken( SequenceInputStream& rStrm ); + bool importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importMemAreaToken( SequenceInputStream& rStrm, bool bAddData ); + bool importMemFuncToken( SequenceInputStream& rStrm ); + bool importNameToken( SequenceInputStream& rStrm ); + bool importNameXToken( SequenceInputStream& rStrm ); + bool importFuncToken( SequenceInputStream& rStrm ); + bool importFuncVarToken( SequenceInputStream& rStrm ); + bool importExpToken( SequenceInputStream& rStrm ); + + LinkSheetRange readSheetRange( SequenceInputStream& rStrm ); + + void swapStreamPosition( SequenceInputStream& rStrm ); + void skipMemAreaAddData( SequenceInputStream& rStrm ); + + // convert BIN token and push API operand or operator --------------------- + + bool pushBiff12Name( sal_Int32 nNameId ); + bool pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId ); + bool pushBiff12Function( sal_uInt16 nFuncId ); + bool pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount ); + +private: + ApiParserWrapper maApiParser; /// Wrapper for the API formula parser object. + sal_Int64 mnAddDataPos; /// Current stream position for additional data (tExp, tArray, tMemArea). + bool mbNeedExtRefs; /// True = parser needs initialization of external reference info. +}; + +// ---------------------------------------------------------------------------- + +OoxFormulaParserImpl::OoxFormulaParserImpl( const FormulaParser& rParent ) : + FormulaParserImpl( rParent ), + maApiParser( rParent.getBaseFilter().getModelFactory(), rParent ), + mnAddDataPos( 0 ), + mbNeedExtRefs( true ) +{ +} + +ApiTokenSequence OoxFormulaParserImpl::importOoxFormula( const CellAddress& rBaseAddr, const OUString& rFormulaString ) +{ + if( mbNeedExtRefs ) + { + maApiParser.getParserProperties().setProperty( PROP_ExternalLinks, getExternalLinks().getLinkInfos() ); + mbNeedExtRefs = false; + } + return finalizeTokenArray( maApiParser.parseFormula( rFormulaString, rBaseAddr ) ); +} + +ApiTokenSequence OoxFormulaParserImpl::importBiff12Formula( const CellAddress& rBaseAddr, FormulaType eType, SequenceInputStream& rStrm ) +{ + initializeImport( rBaseAddr, eType ); + + sal_Int32 nFmlaSize = rStrm.readInt32(); + sal_Int64 nFmlaPos = rStrm.tell(); + sal_Int64 nFmlaEndPos = nFmlaPos + nFmlaSize; + + rStrm.seek( nFmlaEndPos ); + sal_Int32 nAddDataSize = rStrm.readInt32(); + mnAddDataPos = rStrm.tell(); + sal_Int64 nAddDataEndPos = mnAddDataPos + nAddDataSize; + rStrm.seek( nFmlaPos ); + + bool bOk = (nFmlaSize >= 0) && (nAddDataSize >= 0); + bool bRelativeAsOffset = mbRelativeAsOffset; + + while( bOk && !rStrm.isEof() && (rStrm.tell() < nFmlaEndPos) ) + { + sal_uInt8 nTokenId; + rStrm >> nTokenId; + sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK; + sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK; + + if( nTokenClass == BIFF_TOKCLASS_NONE ) + { + // base tokens + switch( nBaseId ) + { + case BIFF_TOKID_EXP: bOk = importExpToken( rStrm ); break; + case BIFF_TOKID_ADD: bOk = pushBinaryOperator( OPCODE_ADD ); break; + case BIFF_TOKID_SUB: bOk = pushBinaryOperator( OPCODE_SUB ); break; + case BIFF_TOKID_MUL: bOk = pushBinaryOperator( OPCODE_MULT ); break; + case BIFF_TOKID_DIV: bOk = pushBinaryOperator( OPCODE_DIV ); break; + case BIFF_TOKID_POWER: bOk = pushBinaryOperator( OPCODE_POWER ); break; + case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( OPCODE_CONCAT ); break; + case BIFF_TOKID_LT: bOk = pushBinaryOperator( OPCODE_LESS ); break; + case BIFF_TOKID_LE: bOk = pushBinaryOperator( OPCODE_LESS_EQUAL ); break; + case BIFF_TOKID_EQ: bOk = pushBinaryOperator( OPCODE_EQUAL ); break; + case BIFF_TOKID_GE: bOk = pushBinaryOperator( OPCODE_GREATER_EQUAL ); break; + case BIFF_TOKID_GT: bOk = pushBinaryOperator( OPCODE_GREATER ); break; + case BIFF_TOKID_NE: bOk = pushBinaryOperator( OPCODE_NOT_EQUAL ); break; + case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( OPCODE_INTERSECT ); break; + case BIFF_TOKID_LIST: bOk = pushBinaryOperator( OPCODE_LIST ); break; + case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( OPCODE_RANGE ); break; + case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( OPCODE_PLUS_SIGN ); break; + case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( OPCODE_MINUS_SIGN ); break; + case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( OPCODE_PERCENT ); break; + case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break; + case BIFF_TOKID_MISSARG: bOk = pushOperand( OPCODE_MISSING ); break; + case BIFF_TOKID_STR: bOk = pushValueOperand( BiffHelper::readString( rStrm, false ) ); break; + case BIFF_TOKID_NLR: bOk = importTableToken( rStrm ); break; + case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break; + case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_BOOL: bOk = pushBiffBoolOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break; + case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break; + default: bOk = false; + } + } + else + { + // classified tokens + switch( nBaseId ) + { + case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break; + case BIFF_TOKID_FUNC: bOk = importFuncToken( rStrm ); break; + case BIFF_TOKID_FUNCVAR: bOk = importFuncVarToken( rStrm ); break; + case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break; + case BIFF_TOKID_REF: bOk = importRefToken( rStrm, false, false ); break; + case BIFF_TOKID_AREA: bOk = importAreaToken( rStrm, false, false ); break; + case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break; + case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_REFERR: bOk = importRefToken( rStrm, true, false ); break; + case BIFF_TOKID_AREAERR: bOk = importAreaToken( rStrm, true, false ); break; + case BIFF_TOKID_REFN: bOk = importRefToken( rStrm, false, true ); break; + case BIFF_TOKID_AREAN: bOk = importAreaToken( rStrm, false, true ); break; + case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_NAMEX: bOk = importNameXToken( rStrm ); break; + case BIFF_TOKID_REF3D: bOk = importRef3dToken( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_AREA3D: bOk = importArea3dToken( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_REFERR3D: bOk = importRef3dToken( rStrm, true, bRelativeAsOffset ); break; + case BIFF_TOKID_AREAERR3D: bOk = importArea3dToken( rStrm, true, bRelativeAsOffset ); break; + default: bOk = false; + } + } + } + + // build and finalize the token sequence + ApiTokenSequence aFinalTokens; + if( bOk && (rStrm.tell() == nFmlaEndPos) && (mnAddDataPos == nAddDataEndPos) ) + aFinalTokens = finalizeImport(); + + // seek behind token array + if( (nFmlaSize >= 0) && (nAddDataSize >= 0) ) + rStrm.seek( nAddDataEndPos ); + + // return the final token sequence + return aFinalTokens; +} + +// import token contents and create API formula token ------------------------- + +bool OoxFormulaParserImpl::importAttrToken( SequenceInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nType; + rStrm >> nType; + // equal flags in all BIFFs + switch( nType ) + { + case 0: // sometimes, tAttrSkip tokens miss the type flag + case BIFF_TOK_ATTR_VOLATILE: + case BIFF_TOK_ATTR_IF: + case BIFF_TOK_ATTR_SKIP: + case BIFF_TOK_ATTR_ASSIGN: + case BIFF_TOK_ATTR_IFERROR: + rStrm.skip( 2 ); + break; + case BIFF_TOK_ATTR_CHOOSE: + rStrm.skip( 2 * rStrm.readuInt16() + 2 ); + break; + case BIFF_TOK_ATTR_SUM: + rStrm.skip( 2 ); + bOk = pushBiff12Function( BIFF_FUNC_SUM, 1 ); + break; + case BIFF_TOK_ATTR_SPACE: + case BIFF_TOK_ATTR_SPACE_VOLATILE: + bOk = importSpaceToken( rStrm ); + break; + default: + bOk = false; + } + return bOk; +} + +bool OoxFormulaParserImpl::importSpaceToken( SequenceInputStream& rStrm ) +{ + // equal constants in BIFF and OOX + sal_uInt8 nType, nCount; + rStrm >> nType >> nCount; + switch( nType ) + { + case BIFF_TOK_ATTR_SPACE_SP: + appendLeadingSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR: + appendLeadingSpaces( nCount, true ); + break; + case BIFF_TOK_ATTR_SPACE_SP_OPEN: + appendOpeningSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR_OPEN: + appendOpeningSpaces( nCount, true ); + break; + case BIFF_TOK_ATTR_SPACE_SP_CLOSE: + appendClosingSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR_CLOSE: + appendClosingSpaces( nCount, true ); + break; + } + return true; +} + +bool OoxFormulaParserImpl::importTableToken( SequenceInputStream& rStrm ) +{ + sal_uInt16 nFlags, nTableId, nCol1, nCol2; + rStrm.skip( 3 ); + rStrm >> nFlags >> nTableId; + rStrm.skip( 2 ); + rStrm >> nCol1 >> nCol2; + TableRef xTable = getTables().getTable( nTableId ); + sal_Int32 nTokenIndex = xTable.get() ? xTable->getTokenIndex() : -1; + if( nTokenIndex >= 0 ) + { + sal_Int32 nWidth = xTable->getWidth(); + sal_Int32 nHeight = xTable->getHeight(); + sal_Int32 nStartCol = 0; + sal_Int32 nEndCol = nWidth - 1; + sal_Int32 nStartRow = 0; + sal_Int32 nEndRow = nHeight - 1; + bool bFixedStartRow = true; + bool bFixedHeight = false; + + bool bSingleCol = getFlag( nFlags, BIFF12_TOK_TABLE_COLUMN ); + bool bColRange = getFlag( nFlags, BIFF12_TOK_TABLE_COLRANGE ); + bool bValidRef = !bSingleCol || !bColRange; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - illegal combination of single column and column range" ); + if( bValidRef ) + { + if( bSingleCol ) + nStartCol = nEndCol = nCol1; + else if( bColRange ) + { nStartCol = nCol1; nEndCol = nCol2; } + bValidRef = (nStartCol <= nEndCol) && (nEndCol < nWidth); + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid column range" ); + } + + if( bValidRef ) + { + bool bAllRows = getFlag( nFlags, BIFF12_TOK_TABLE_ALL ); + bool bHeaderRows = getFlag( nFlags, BIFF12_TOK_TABLE_HEADERS ); + bool bDataRows = getFlag( nFlags, BIFF12_TOK_TABLE_DATA ); + bool bTotalsRows = getFlag( nFlags, BIFF12_TOK_TABLE_TOTALS ); + bool bThisRow = getFlag( nFlags, BIFF12_TOK_TABLE_THISROW ); + + sal_Int32 nStartDataRow = xTable->getHeaderRows(); + sal_Int32 nEndDataRow = nEndRow - xTable->getTotalsRows(); + bValidRef = (nStartRow <= nStartDataRow) && (nStartDataRow <= nEndDataRow) && (nEndDataRow <= nEndRow); + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid data row range" ); + if( bValidRef ) + { + if( bAllRows ) + { + bValidRef = !bHeaderRows && !bDataRows && !bTotalsRows && !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#All] table token" ); + } + else if( bHeaderRows ) + { + bValidRef = !bTotalsRows && !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Headers] table token" ); + nEndRow = bDataRows ? nEndDataRow : (nStartDataRow - 1); + bFixedHeight = !bDataRows; + } + else if( bDataRows ) + { + bValidRef = !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Data] table token" ); + nStartRow = nStartDataRow; + if( !bTotalsRows ) nEndRow = nEndDataRow; + } + else if( bTotalsRows ) + { + bValidRef = !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Totals] table token" ); + nStartRow = nEndDataRow + 1; + bFixedStartRow = false; + bFixedHeight = !bDataRows; + } + else if( bThisRow ) + { + nStartRow = nEndRow = maBaseAddr.Row - xTable->getRange().StartRow; + bFixedHeight = true; + } + else + { + // nothing is the same as [#Data] + nStartRow = nStartDataRow; + nEndRow = nEndDataRow; + } + } + if( bValidRef ) + bValidRef = (0 <= nStartRow) && (nStartRow <= nEndRow) && (nEndRow < nHeight); + } + if( bValidRef ) + { + // push single database area token, if table token refers to entire table + if( (nStartCol == 0) && (nEndCol + 1 == nWidth) && (nStartRow == 0) && (nEndRow + 1 == nHeight) ) + return pushValueOperand( nTokenIndex, OPCODE_DBAREA ); + // create an OFFSET function call to refer to a subrange of the table + const FunctionInfo* pRowsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_ROWS ); + const FunctionInfo* pColumnsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_COLUMNS ); + return + pRowsInfo && pColumnsInfo && + pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) && + (bFixedStartRow ? + pushValueOperandToken< double >( nStartRow ) : + (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) && + pushFunctionOperatorToken( *pRowsInfo, 1 ) && + pushValueOperandToken< double >( nHeight - nStartRow ) && + pushBinaryOperatorToken( OPCODE_SUB ))) && + pushValueOperandToken< double >( nStartCol ) && + (bFixedHeight ? + pushValueOperandToken< double >( nEndRow - nStartRow + 1 ) : + (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) && + pushFunctionOperatorToken( *pRowsInfo, 1 ) && + (((nStartRow == 0) && (nEndRow + 1 == nHeight)) || + (pushValueOperandToken< double >( nHeight - (nEndRow - nStartRow + 1) ) && + pushBinaryOperatorToken( OPCODE_SUB ))))) && + (((nStartCol == 0) && (nEndCol + 1 == nWidth)) ? + (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) && + pushFunctionOperatorToken( *pColumnsInfo, 1 )) : + pushValueOperandToken< double >( nEndCol - nStartCol + 1 )) && + pushBiff12Function( BIFF_FUNC_OFFSET, 5 ); + } + } + return pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool OoxFormulaParserImpl::importArrayToken( SequenceInputStream& rStrm ) +{ + rStrm.skip( 14 ); + + // start token array with opening brace and leading spaces + pushOperand( OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = getFormulaSize(); + + // read array size + swapStreamPosition( rStrm ); + sal_Int32 nRows = rStrm.readInt32(); + sal_Int32 nCols = rStrm.readInt32(); + OSL_ENSURE( (nCols > 0) && (nRows > 0), "OoxFormulaParserImpl::importArrayToken - empty array" ); + + // read array values and build token array + for( sal_Int32 nRow = 0; !rStrm.isEof() && (nRow < nRows); ++nRow ) + { + if( nRow > 0 ) + appendRawToken( OPCODE_ARRAY_ROWSEP ); + for( sal_Int32 nCol = 0; !rStrm.isEof() && (nCol < nCols); ++nCol ) + { + if( nCol > 0 ) + appendRawToken( OPCODE_ARRAY_COLSEP ); + switch( rStrm.readuInt8() ) + { + case BIFF_TOK_ARRAY_DOUBLE: + appendRawToken( OPCODE_PUSH ) <<= rStrm.readDouble(); + break; + case BIFF_TOK_ARRAY_STRING: + appendRawToken( OPCODE_PUSH ) <<= BiffHelper::readString( rStrm, false ); + break; + case BIFF_TOK_ARRAY_BOOL: + appendRawToken( OPCODE_PUSH ) <<= (static_cast< double >( (rStrm.readuInt8() == BIFF_TOK_BOOL_FALSE) ? 0.0 : 1.0 )); + break; + case BIFF_TOK_ARRAY_ERROR: + appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() ); + rStrm.skip( 3 ); + break; + default: + OSL_FAIL( "OoxFormulaParserImpl::importArrayToken - unknown data type" ); + appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA ); + } + } + } + swapStreamPosition( rStrm ); + + // close token array and set resulting operand size + appendRawToken( OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize ); + return true; +} + +bool OoxFormulaParserImpl::importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readBiff12Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readBiff12Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange( rStrm ); + BinSingleRef2d aRef; + aRef.readBiff12Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange( rStrm ); + BinComplexRef2d aRef; + aRef.readBiff12Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importMemAreaToken( SequenceInputStream& rStrm, bool bAddData ) +{ + rStrm.skip( 6 ); + if( bAddData ) + skipMemAreaAddData( rStrm ); + return true; +} + +bool OoxFormulaParserImpl::importMemFuncToken( SequenceInputStream& rStrm ) +{ + rStrm.skip( 2 ); + return true; +} + +bool OoxFormulaParserImpl::importNameToken( SequenceInputStream& rStrm ) +{ + return pushBiff12Name( rStrm.readInt32() ); +} + +bool OoxFormulaParserImpl::importNameXToken( SequenceInputStream& rStrm ) +{ + sal_Int32 nRefId = rStrm.readInt16(); + sal_Int32 nNameId = rStrm.readInt32(); + return pushBiff12ExtName( nRefId, nNameId ); +} + +bool OoxFormulaParserImpl::importFuncToken( SequenceInputStream& rStrm ) +{ + sal_uInt16 nFuncId; + rStrm >> nFuncId; + return pushBiff12Function( nFuncId ); +} + +bool OoxFormulaParserImpl::importFuncVarToken( SequenceInputStream& rStrm ) +{ + sal_uInt8 nParamCount; + sal_uInt16 nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushBiff12Function( nFuncId, nParamCount ); +} + +bool OoxFormulaParserImpl::importExpToken( SequenceInputStream& rStrm ) +{ + BinAddress aBaseAddr; + rStrm >> aBaseAddr.mnRow; + swapStreamPosition( rStrm ); + rStrm >> aBaseAddr.mnCol; + swapStreamPosition( rStrm ); + return pushSpecialTokenOperand( aBaseAddr, false ); +} + +LinkSheetRange OoxFormulaParserImpl::readSheetRange( SequenceInputStream& rStrm ) +{ + return getExternalLinks().getSheetRange( rStrm.readInt16() ); +} + +void OoxFormulaParserImpl::swapStreamPosition( SequenceInputStream& rStrm ) +{ + sal_Int64 nRecPos = rStrm.tell(); + rStrm.seek( mnAddDataPos ); + mnAddDataPos = nRecPos; +} + +void OoxFormulaParserImpl::skipMemAreaAddData( SequenceInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + rStrm.skip( 16 * rStrm.readInt32() ); + swapStreamPosition( rStrm ); +} + +// convert BIN token and push API operand or operator ------------------------- + +bool OoxFormulaParserImpl::pushBiff12Name( sal_Int32 nNameId ) +{ + // one-based in BIFF12 formulas + return pushDefinedNameOperand( getDefinedNames().getByIndex( nNameId - 1 ) ); +} + +bool OoxFormulaParserImpl::pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId ) +{ + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + { + if( pExtLink->getLinkType() == LINKTYPE_SELF ) + return pushBiff12Name( nNameId ); + // external name indexes are one-based in BIFF12 + ExternalNameRef xExtName = pExtLink->getNameByIndex( nNameId - 1 ); + return pushExternalNameOperand( xExtName, *pExtLink ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId ) +{ + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) ) + if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount ) + return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount ); + return pushFunctionOperator( OPCODE_NONAME, 0 ); +} + +bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount ) +{ + if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) ) + nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK; + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) ) + return pushFunctionOperator( *pFuncInfo, nParamCount ); + return pushFunctionOperator( OPCODE_NONAME, nParamCount ); +} + +// BIFF parser implementation ================================================= + +namespace { + +/** A natural language reference struct with relative flag. */ +struct BiffNlr +{ + sal_Int32 mnCol; /// Column index. + sal_Int32 mnRow; /// Row index. + bool mbRel; /// True = relative column/row reference. + + explicit BiffNlr(); + + void readBiff8Data( BiffInputStream& rStrm ); +}; + +BiffNlr::BiffNlr() : + mnCol( 0 ), + mnRow( 0 ), + mbRel( false ) +{ +} + +void BiffNlr::readBiff8Data( BiffInputStream& rStrm ) +{ + sal_uInt16 nRow, nCol; + rStrm >> nRow >> nCol; + mnCol = nCol & BIFF_TOK_NLR_MASK; + mnRow = nRow; + mbRel = getFlag( nCol, BIFF_TOK_NLR_REL ); +} + +bool lclIsValidNlrStack( const BinAddress& rAddr1, const BinAddress& rAddr2, bool bRow ) +{ + return bRow ? + ((rAddr1.mnRow == rAddr2.mnRow) && (rAddr1.mnCol + 1 == rAddr2.mnCol)) : + ((rAddr1.mnCol == rAddr2.mnCol) && (rAddr1.mnRow + 1 == rAddr2.mnRow)); +} + +bool lclIsValidNlrRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ) +{ + return bRow ? + ((rNlr.mnRow == rRange.maFirst.mnRow) && (rNlr.mnCol + 1 == rRange.maFirst.mnCol) && (rRange.maFirst.mnRow == rRange.maLast.mnRow)) : + ((rNlr.mnCol == rRange.maFirst.mnCol) && (rNlr.mnRow + 1 == rRange.maFirst.mnRow) && (rRange.maFirst.mnCol == rRange.maLast.mnCol)); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +class BiffFormulaParserImpl : public FormulaParserImpl +{ +public: + explicit BiffFormulaParserImpl( const FormulaParser& rParent ); + + virtual ApiTokenSequence importBiffFormula( + const CellAddress& rBaseAddr, + FormulaType eType, + BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ); + +private: + // import token contents and create API formula token --------------------- + + bool importTokenNotAvailable( BiffInputStream& rStrm ); + bool importRefTokenNotAvailable( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importStrToken2( BiffInputStream& rStrm ); + bool importStrToken8( BiffInputStream& rStrm ); + bool importAttrToken( BiffInputStream& rStrm ); + bool importSpaceToken3( BiffInputStream& rStrm ); + bool importSpaceToken4( BiffInputStream& rStrm ); + bool importSheetToken2( BiffInputStream& rStrm ); + bool importSheetToken3( BiffInputStream& rStrm ); + bool importEndSheetToken2( BiffInputStream& rStrm ); + bool importEndSheetToken3( BiffInputStream& rStrm ); + bool importNlrToken( BiffInputStream& rStrm ); + bool importArrayToken( BiffInputStream& rStrm ); + bool importRefToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRefToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importMemAreaToken( BiffInputStream& rStrm, bool bAddData ); + bool importMemFuncToken( BiffInputStream& rStrm ); + bool importNameToken( BiffInputStream& rStrm ); + bool importNameXToken( BiffInputStream& rStrm ); + bool importFuncToken2( BiffInputStream& rStrm ); + bool importFuncToken4( BiffInputStream& rStrm ); + bool importFuncVarToken2( BiffInputStream& rStrm ); + bool importFuncVarToken4( BiffInputStream& rStrm ); + bool importFuncCEToken( BiffInputStream& rStrm ); + bool importExpToken( BiffInputStream& rStrm ); + bool importTblToken( BiffInputStream& rStrm ); + + bool importNlrAddrToken( BiffInputStream& rStrm, bool bRow ); + bool importNlrRangeToken( BiffInputStream& rStrm ); + bool importNlrSAddrToken( BiffInputStream& rStrm, bool bRow ); + bool importNlrSRangeToken( BiffInputStream& rStrm ); + bool importNlrErrToken( BiffInputStream& rStrm, sal_uInt16 nSkip ); + + sal_Int32 readRefId( BiffInputStream& rStrm ); + sal_uInt16 readNameId( BiffInputStream& rStrm ); + LinkSheetRange readSheetRange5( BiffInputStream& rStrm ); + LinkSheetRange readSheetRange8( BiffInputStream& rStrm ); + + void swapStreamPosition( BiffInputStream& rStrm ); + void skipMemAreaAddData( BiffInputStream& rStrm ); + bool readNlrSAddrAddData( BiffNlr& orNlr, BiffInputStream& rStrm, bool bRow ); + bool readNlrSRangeAddData( BiffNlr& orNlr, bool& orbIsRow, BiffInputStream& rStrm ); + + // convert BIFF token and push API operand or operator -------------------- + + bool pushBiffReference( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushBiffReference( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushBiffNlrAddr( const BiffNlr& rNlr, bool bRow ); + bool pushBiffNlrRange( const BiffNlr& rNlr, const BinRange& rRange ); + bool pushBiffNlrSAddr( const BiffNlr& rNlr, bool bRow ); + bool pushBiffNlrSRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ); + bool pushBiffName( sal_uInt16 nNameId ); + bool pushBiffExtName( sal_Int32 nRefId, sal_uInt16 nNameId ); + bool pushBiffFunction( sal_uInt16 nFuncId ); + bool pushBiffFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ); + + // ------------------------------------------------------------------------ +private: + typedef bool (BiffFormulaParserImpl::*ImportTokenFunc)( BiffInputStream& ); + typedef bool (BiffFormulaParserImpl::*ImportRefTokenFunc)( BiffInputStream&, bool, bool ); + + ImportTokenFunc mpImportStrToken; /// Pointer to tStr import function (string constant). + ImportTokenFunc mpImportSpaceToken; /// Pointer to tAttrSpace import function (spaces/line breaks). + ImportTokenFunc mpImportSheetToken; /// Pointer to tSheet import function (external reference). + ImportTokenFunc mpImportEndSheetToken; /// Pointer to tEndSheet import function (end of external reference). + ImportTokenFunc mpImportNlrToken; /// Pointer to tNlr import function (natural language reference). + ImportRefTokenFunc mpImportRefToken; /// Pointer to tRef import function (2d cell reference). + ImportRefTokenFunc mpImportAreaToken; /// Pointer to tArea import function (2d area reference). + ImportRefTokenFunc mpImportRef3dToken; /// Pointer to tRef3d import function (3d cell reference). + ImportRefTokenFunc mpImportArea3dToken; /// Pointer to tArea3d import function (3d area reference). + ImportTokenFunc mpImportNameXToken; /// Pointer to tNameX import function (external name). + ImportTokenFunc mpImportFuncToken; /// Pointer to tFunc import function (function with fixed parameter count). + ImportTokenFunc mpImportFuncVarToken; /// Pointer to tFuncVar import function (function with variable parameter count). + ImportTokenFunc mpImportFuncCEToken; /// Pointer to tFuncCE import function (command macro call). + sal_Int64 mnAddDataPos; /// Current stream position for additional data (tArray, tMemArea, tNlr). + sal_Int32 mnCurrRefId; /// Current ref-id from tSheet token (BIFF2-BIFF4 only). + sal_uInt16 mnAttrDataSize; /// Size of one tAttr data element. + sal_uInt16 mnArraySize; /// Size of tArray data. + sal_uInt16 mnNameSize; /// Size of tName data. + sal_uInt16 mnMemAreaSize; /// Size of tMemArea data. + sal_uInt16 mnMemFuncSize; /// Size of tMemFunc data. + sal_uInt16 mnRefIdSize; /// Size of unused data following a reference identifier. +}; + +// ---------------------------------------------------------------------------- + +BiffFormulaParserImpl::BiffFormulaParserImpl( const FormulaParser& rParent ) : + FormulaParserImpl( rParent ), + mnAddDataPos( 0 ), + mnCurrRefId( 0 ) +{ + switch( getBiff() ) + { + case BIFF2: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken2; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken2; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken2; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken2; + mpImportFuncCEToken = &BiffFormulaParserImpl::importFuncCEToken; + mnAttrDataSize = 1; + mnArraySize = 6; + mnNameSize = 5; + mnMemAreaSize = 4; + mnMemFuncSize = 1; + mnRefIdSize = 1; + break; + case BIFF3: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken3; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken3; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken3; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken2; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken2; + mpImportFuncCEToken = &BiffFormulaParserImpl::importFuncCEToken; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 8; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 2; + break; + case BIFF4: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken3; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken3; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 8; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 2; + break; + case BIFF5: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportEndSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRef3dToken5; + mpImportArea3dToken = &BiffFormulaParserImpl::importArea3dToken5; + mpImportNameXToken = &BiffFormulaParserImpl::importNameXToken; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 12; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 8; + break; + case BIFF8: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken8; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportEndSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportNlrToken = &BiffFormulaParserImpl::importNlrToken; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken8; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken8; + mpImportRef3dToken = &BiffFormulaParserImpl::importRef3dToken8; + mpImportArea3dToken = &BiffFormulaParserImpl::importArea3dToken8; + mpImportNameXToken = &BiffFormulaParserImpl::importNameXToken; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 2; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 0; + break; + case BIFF_UNKNOWN: break; + } +} + +ApiTokenSequence BiffFormulaParserImpl::importBiffFormula( const CellAddress& rBaseAddr, + FormulaType eType, BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ) +{ + initializeImport( rBaseAddr, eType ); + mnCurrRefId = 0; + + sal_uInt16 nFmlaSize = lclReadFmlaSize( rStrm, getBiff(), pnFmlaSize ); + sal_Int64 nEndPos = mnAddDataPos = rStrm.tell() + nFmlaSize; + + bool bOk = true; + while( bOk && !rStrm.isEof() && (rStrm.tell() < nEndPos) ) + { + sal_uInt8 nTokenId; + rStrm >> nTokenId; + sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK; + sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK; + + bOk = !getFlag( nTokenId, BIFF_TOKFLAG_INVALID ); + if( bOk ) + { + if( nTokenClass == BIFF_TOKCLASS_NONE ) + { + // base tokens + switch( nBaseId ) + { + case BIFF_TOKID_EXP: bOk = importExpToken( rStrm ); break; + case BIFF_TOKID_TBL: bOk = importTblToken( rStrm ); break; + case BIFF_TOKID_ADD: bOk = pushBinaryOperator( OPCODE_ADD ); break; + case BIFF_TOKID_SUB: bOk = pushBinaryOperator( OPCODE_SUB ); break; + case BIFF_TOKID_MUL: bOk = pushBinaryOperator( OPCODE_MULT ); break; + case BIFF_TOKID_DIV: bOk = pushBinaryOperator( OPCODE_DIV ); break; + case BIFF_TOKID_POWER: bOk = pushBinaryOperator( OPCODE_POWER ); break; + case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( OPCODE_CONCAT ); break; + case BIFF_TOKID_LT: bOk = pushBinaryOperator( OPCODE_LESS ); break; + case BIFF_TOKID_LE: bOk = pushBinaryOperator( OPCODE_LESS_EQUAL ); break; + case BIFF_TOKID_EQ: bOk = pushBinaryOperator( OPCODE_EQUAL ); break; + case BIFF_TOKID_GE: bOk = pushBinaryOperator( OPCODE_GREATER_EQUAL ); break; + case BIFF_TOKID_GT: bOk = pushBinaryOperator( OPCODE_GREATER ); break; + case BIFF_TOKID_NE: bOk = pushBinaryOperator( OPCODE_NOT_EQUAL ); break; + case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( OPCODE_INTERSECT ); break; + case BIFF_TOKID_LIST: bOk = pushBinaryOperator( OPCODE_LIST ); break; + case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( OPCODE_RANGE ); break; + case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( OPCODE_PLUS_SIGN ); break; + case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( OPCODE_MINUS_SIGN ); break; + case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( OPCODE_PERCENT ); break; + case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break; + case BIFF_TOKID_MISSARG: bOk = pushOperand( OPCODE_MISSING ); break; + case BIFF_TOKID_STR: bOk = (this->*mpImportStrToken)( rStrm ); break; + case BIFF_TOKID_NLR: bOk = (this->*mpImportNlrToken)( rStrm ); break; + case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break; + case BIFF_TOKID_SHEET: bOk = (this->*mpImportSheetToken)( rStrm ); break; + case BIFF_TOKID_ENDSHEET: bOk = (this->*mpImportEndSheetToken)( rStrm ); break; + case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_BOOL: bOk = pushBiffBoolOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break; + case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break; + default: bOk = false; + } + } + else + { + // classified tokens + switch( nBaseId ) + { + case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break; + case BIFF_TOKID_FUNC: bOk = (this->*mpImportFuncToken)( rStrm ); break; + case BIFF_TOKID_FUNCVAR: bOk = (this->*mpImportFuncVarToken)( rStrm ); break; + case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break; + case BIFF_TOKID_REF: bOk = (this->*mpImportRefToken)( rStrm, false, false ); break; + case BIFF_TOKID_AREA: bOk = (this->*mpImportAreaToken)( rStrm, false, false ); break; + case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break; + case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_REFERR: bOk = (this->*mpImportRefToken)( rStrm, true, false ); break; + case BIFF_TOKID_AREAERR: bOk = (this->*mpImportAreaToken)( rStrm, true, false ); break; + case BIFF_TOKID_REFN: bOk = (this->*mpImportRefToken)( rStrm, false, true ); break; + case BIFF_TOKID_AREAN: bOk = (this->*mpImportAreaToken)( rStrm, false, true ); break; + case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_FUNCCE: bOk = (this->*mpImportFuncCEToken)( rStrm ); break; + case BIFF_TOKID_NAMEX: bOk = (this->*mpImportNameXToken)( rStrm ); break; + case BIFF_TOKID_REF3D: bOk = (this->*mpImportRef3dToken)( rStrm, false, mbRelativeAsOffset ); break; + case BIFF_TOKID_AREA3D: bOk = (this->*mpImportArea3dToken)( rStrm, false, mbRelativeAsOffset ); break; + case BIFF_TOKID_REFERR3D: bOk = (this->*mpImportRef3dToken)( rStrm, true, mbRelativeAsOffset ); break; + case BIFF_TOKID_AREAERR3D: bOk = (this->*mpImportArea3dToken)( rStrm, true, mbRelativeAsOffset ); break; + default: bOk = false; + } + } + } + } + + // build and finalize the token sequence + ApiTokenSequence aFinalTokens; + if( bOk && (rStrm.tell() == nEndPos) ) + aFinalTokens = finalizeImport(); + + // seek behind additional token data of tArray, tMemArea, tNlr tokens + rStrm.seek( mnAddDataPos ); + + // return the final token sequence + return aFinalTokens; +} + +// import token contents and create API formula token ------------------------- + +bool BiffFormulaParserImpl::importTokenNotAvailable( BiffInputStream& ) +{ + // dummy function for pointer-to-member-function + return false; +} + +bool BiffFormulaParserImpl::importRefTokenNotAvailable( BiffInputStream&, bool, bool ) +{ + // dummy function for pointer-to-member-function + return false; +} + +bool BiffFormulaParserImpl::importStrToken2( BiffInputStream& rStrm ) +{ + return pushValueOperand( rStrm.readByteStringUC( false, getTextEncoding(), mbAllowNulChars ) ); +} + +bool BiffFormulaParserImpl::importStrToken8( BiffInputStream& rStrm ) +{ + // read flags field for empty strings also + return pushValueOperand( rStrm.readUniStringBody( rStrm.readuInt8(), mbAllowNulChars ) ); +} + +bool BiffFormulaParserImpl::importAttrToken( BiffInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nType; + rStrm >> nType; + switch( nType ) + { + case 0: // sometimes, tAttrSkip tokens miss the type flag + case BIFF_TOK_ATTR_VOLATILE: + case BIFF_TOK_ATTR_IF: + case BIFF_TOK_ATTR_SKIP: + case BIFF_TOK_ATTR_ASSIGN: + rStrm.skip( mnAttrDataSize ); + break; + case BIFF_TOK_ATTR_CHOOSE: + rStrm.skip( mnAttrDataSize * (1 + ((getBiff() == BIFF2) ? rStrm.readuInt8() : rStrm.readuInt16())) ); + break; + case BIFF_TOK_ATTR_SUM: + rStrm.skip( mnAttrDataSize ); + bOk = pushBiffFunction( BIFF_FUNC_SUM, 1 ); + break; + case BIFF_TOK_ATTR_SPACE: + case BIFF_TOK_ATTR_SPACE_VOLATILE: + bOk = (this->*mpImportSpaceToken)( rStrm ); + break; + default: + bOk = false; + } + return bOk; +} + +bool BiffFormulaParserImpl::importSpaceToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 2 ); + return true; +} + +bool BiffFormulaParserImpl::importSpaceToken4( BiffInputStream& rStrm ) +{ + sal_uInt8 nType, nCount; + rStrm >> nType >> nCount; + switch( nType ) + { + case BIFF_TOK_ATTR_SPACE_SP: + appendLeadingSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR: + appendLeadingSpaces( nCount, true ); + break; + case BIFF_TOK_ATTR_SPACE_SP_OPEN: + appendOpeningSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR_OPEN: + appendOpeningSpaces( nCount, true ); + break; + case BIFF_TOK_ATTR_SPACE_SP_CLOSE: + appendClosingSpaces( nCount, false ); + break; + case BIFF_TOK_ATTR_SPACE_BR_CLOSE: + appendClosingSpaces( nCount, true ); + break; + } + return true; +} + +bool BiffFormulaParserImpl::importSheetToken2( BiffInputStream& rStrm ) +{ + rStrm.skip( 4 ); + mnCurrRefId = readRefId( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importSheetToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 6 ); + mnCurrRefId = readRefId( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importEndSheetToken2( BiffInputStream& rStrm ) +{ + rStrm.skip( 3 ); + mnCurrRefId = 0; + return true; +} + +bool BiffFormulaParserImpl::importEndSheetToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 4 ); + mnCurrRefId = 0; + return true; +} + +bool BiffFormulaParserImpl::importNlrToken( BiffInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nNlrType; + rStrm >> nNlrType; + switch( nNlrType ) + { + case BIFF_TOK_NLR_ERR: bOk = importNlrErrToken( rStrm, 4 ); break; + case BIFF_TOK_NLR_ROWR: bOk = importNlrAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_COLR: bOk = importNlrAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_ROWV: bOk = importNlrAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_COLV: bOk = importNlrAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_RANGE: bOk = importNlrRangeToken( rStrm ); break; + case BIFF_TOK_NLR_SRANGE: bOk = importNlrSRangeToken( rStrm ); break; + case BIFF_TOK_NLR_SROWR: bOk = importNlrSAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_SCOLR: bOk = importNlrSAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_SROWV: bOk = importNlrSAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_SCOLV: bOk = importNlrSAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_RANGEERR: bOk = importNlrErrToken( rStrm, 13 ); break; + case BIFF_TOK_NLR_SXNAME: bOk = importNlrErrToken( rStrm, 4 ); break; + default: bOk = false; + } + return bOk; +} + +bool BiffFormulaParserImpl::importArrayToken( BiffInputStream& rStrm ) +{ + rStrm.skip( mnArraySize ); + + // start token array with opening brace and leading spaces + pushOperand( OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = getFormulaSize(); + bool bBiff8 = getBiff() == BIFF8; + + // read array size + swapStreamPosition( rStrm ); + sal_uInt16 nCols = rStrm.readuInt8(); + sal_uInt16 nRows = rStrm.readuInt16(); + if( bBiff8 ) { ++nCols; ++nRows; } else if( nCols == 0 ) nCols = 256; + OSL_ENSURE( (nCols > 0) && (nRows > 0), "BiffFormulaParserImpl::importArrayToken - empty array" ); + + // read array values and build token array + for( sal_uInt16 nRow = 0; !rStrm.isEof() && (nRow < nRows); ++nRow ) + { + if( nRow > 0 ) + appendRawToken( OPCODE_ARRAY_ROWSEP ); + for( sal_uInt16 nCol = 0; !rStrm.isEof() && (nCol < nCols); ++nCol ) + { + if( nCol > 0 ) + appendRawToken( OPCODE_ARRAY_COLSEP ); + switch( rStrm.readuInt8() ) + { + case BIFF_DATATYPE_EMPTY: + appendRawToken( OPCODE_PUSH ) <<= OUString(); + rStrm.skip( 8 ); + break; + case BIFF_DATATYPE_DOUBLE: + appendRawToken( OPCODE_PUSH ) <<= rStrm.readDouble(); + break; + case BIFF_DATATYPE_STRING: + appendRawToken( OPCODE_PUSH ) <<= bBiff8 ? + rStrm.readUniString( mbAllowNulChars ) : + rStrm.readByteStringUC( false, getTextEncoding(), mbAllowNulChars ); + break; + case BIFF_DATATYPE_BOOL: + appendRawToken( OPCODE_PUSH ) <<= (static_cast< double >( (rStrm.readuInt8() == BIFF_TOK_BOOL_FALSE) ? 0.0 : 1.0 )); + rStrm.skip( 7 ); + break; + case BIFF_DATATYPE_ERROR: + appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() ); + rStrm.skip( 7 ); + break; + default: + OSL_FAIL( "BiffFormulaParserImpl::importArrayToken - unknown data type" ); + appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA ); + } + } + } + swapStreamPosition( rStrm ); + + // close token array and set resulting operand size + appendRawToken( OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize ); + return true; +} + +bool BiffFormulaParserImpl::importRefToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRefToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importAreaToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importAreaToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRef3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange5( rStrm ); + BinSingleRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRef3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange8( rStrm ); + BinSingleRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importArea3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange5( rStrm ); + BinComplexRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importArea3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange8( rStrm ); + BinComplexRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importMemAreaToken( BiffInputStream& rStrm, bool bAddData ) +{ + rStrm.skip( mnMemAreaSize ); + if( bAddData ) + skipMemAreaAddData( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importMemFuncToken( BiffInputStream& rStrm ) +{ + rStrm.skip( mnMemFuncSize ); + return true; +} + +bool BiffFormulaParserImpl::importNameToken( BiffInputStream& rStrm ) +{ + sal_uInt16 nNameId = readNameId( rStrm ); + return (mnCurrRefId > 0) ? pushBiffExtName( mnCurrRefId, nNameId ) : pushBiffName( nNameId ); +} + +bool BiffFormulaParserImpl::importNameXToken( BiffInputStream& rStrm ) +{ + sal_Int32 nRefId = readRefId( rStrm ); + sal_uInt16 nNameId = readNameId( rStrm ); + return pushBiffExtName( nRefId, nNameId ); +} + +bool BiffFormulaParserImpl::importFuncToken2( BiffInputStream& rStrm ) +{ + sal_uInt8 nFuncId; + rStrm >> nFuncId; + return pushBiffFunction( nFuncId ); +} + +bool BiffFormulaParserImpl::importFuncToken4( BiffInputStream& rStrm ) +{ + sal_uInt16 nFuncId; + rStrm >> nFuncId; + return pushBiffFunction( nFuncId ); +} + +bool BiffFormulaParserImpl::importFuncVarToken2( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount, nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushBiffFunction( nFuncId, nParamCount ); +} + +bool BiffFormulaParserImpl::importFuncVarToken4( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount; + sal_uInt16 nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushBiffFunction( nFuncId, nParamCount & BIFF_TOK_FUNCVAR_COUNTMASK ); +} + +bool BiffFormulaParserImpl::importFuncCEToken( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount, nFuncId; + rStrm >> nParamCount >> nFuncId; + sal_uInt16 nCmdId = nFuncId; + setFlag( nCmdId, BIFF_TOK_FUNCVAR_CMD ); + return pushBiffFunction( nCmdId, nParamCount ); +} + +bool BiffFormulaParserImpl::importExpToken( BiffInputStream& rStrm ) +{ + BinAddress aBaseAddr; + aBaseAddr.read( rStrm ); + return pushSpecialTokenOperand( aBaseAddr, false ); +} + +bool BiffFormulaParserImpl::importTblToken( BiffInputStream& rStrm ) +{ + BinAddress aBaseAddr; + aBaseAddr.read( rStrm ); + return pushSpecialTokenOperand( aBaseAddr, true ); +} + +bool BiffFormulaParserImpl::importNlrAddrToken( BiffInputStream& rStrm, bool bRow ) +{ + BiffNlr aNlr; + aNlr.readBiff8Data( rStrm ); + return pushBiffNlrAddr( aNlr, bRow ); +} + +bool BiffFormulaParserImpl::importNlrRangeToken( BiffInputStream& rStrm ) +{ + BiffNlr aNlr; + aNlr.readBiff8Data( rStrm ); + rStrm.skip( 1 ); + BinRange aRange; + rStrm >> aRange; + return pushBiffNlrRange( aNlr, aRange ); +} + +bool BiffFormulaParserImpl::importNlrSAddrToken( BiffInputStream& rStrm, bool bRow ) +{ + rStrm.skip( 4 ); + BiffNlr aNlr; + return readNlrSAddrAddData( aNlr, rStrm, bRow ) ? pushBiffNlrSAddr( aNlr, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::importNlrSRangeToken( BiffInputStream& rStrm ) +{ + rStrm.skip( 5 ); + BinRange aRange; + rStrm >> aRange; + BiffNlr aNlr; + bool bRow; + return readNlrSRangeAddData( aNlr, bRow, rStrm ) ? pushBiffNlrSRange( aNlr, aRange, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::importNlrErrToken( BiffInputStream& rStrm, sal_uInt16 nIgnore ) +{ + rStrm.skip( nIgnore ); + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +sal_Int32 BiffFormulaParserImpl::readRefId( BiffInputStream& rStrm ) +{ + sal_Int16 nRefId; + rStrm >> nRefId; + rStrm.skip( mnRefIdSize ); + return nRefId; +} + +sal_uInt16 BiffFormulaParserImpl::readNameId( BiffInputStream& rStrm ) +{ + sal_uInt16 nNameId; + rStrm >> nNameId; + rStrm.skip( mnNameSize ); + return nNameId; +} + +LinkSheetRange BiffFormulaParserImpl::readSheetRange5( BiffInputStream& rStrm ) +{ + sal_Int32 nRefId = readRefId( rStrm ); + sal_Int16 nTab1, nTab2; + rStrm >> nTab1 >> nTab2; + return getExternalLinks().getSheetRange( nRefId, nTab1, nTab2 ); +} + +LinkSheetRange BiffFormulaParserImpl::readSheetRange8( BiffInputStream& rStrm ) +{ + return getExternalLinks().getSheetRange( readRefId( rStrm ) ); +} + +void BiffFormulaParserImpl::swapStreamPosition( BiffInputStream& rStrm ) +{ + sal_Int64 nRecPos = rStrm.tell(); + rStrm.seek( mnAddDataPos ); + mnAddDataPos = nRecPos; +} + +void BiffFormulaParserImpl::skipMemAreaAddData( BiffInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + sal_Int32 nCount = rStrm.readuInt16(); + rStrm.skip( ((getBiff() == BIFF8) ? 8 : 6) * nCount ); + swapStreamPosition( rStrm ); +} + +bool BiffFormulaParserImpl::readNlrSAddrAddData( BiffNlr& orNlr, BiffInputStream& rStrm, bool bRow ) +{ + bool bIsRow; + return readNlrSRangeAddData( orNlr, bIsRow, rStrm ) && (bIsRow == bRow); +} + +bool BiffFormulaParserImpl::readNlrSRangeAddData( BiffNlr& orNlr, bool& orbIsRow, BiffInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + // read number of cell addresses and relative flag + sal_uInt32 nCount; + rStrm >> nCount; + bool bRel = getFlag( nCount, BIFF_TOK_NLR_ADDREL ); + nCount &= BIFF_TOK_NLR_ADDMASK; + sal_Int64 nEndPos = rStrm.tell() + 4 * nCount; + // read list of cell addresses + bool bValid = false; + if( nCount >= 2 ) + { + // detect column/row orientation + BinAddress aAddr1, aAddr2; + rStrm >> aAddr1 >> aAddr2; + orbIsRow = aAddr1.mnRow == aAddr2.mnRow; + bValid = lclIsValidNlrStack( aAddr1, aAddr2, orbIsRow ); + // read and verify additional cell positions + for( sal_uInt32 nIndex = 2; bValid && (nIndex < nCount); ++nIndex ) + { + aAddr1 = aAddr2; + rStrm >> aAddr2; + bValid = !rStrm.isEof() && lclIsValidNlrStack( aAddr1, aAddr2, orbIsRow ); + } + // check that last imported position (aAddr2) is not at the end of the sheet + bValid = bValid && (orbIsRow ? (aAddr2.mnCol < mnMaxApiCol) : (aAddr2.mnRow < mnMaxApiRow)); + // fill the NLR struct with the last imported position + if( bValid ) + { + orNlr.mnCol = aAddr2.mnCol; + orNlr.mnRow = aAddr2.mnRow; + orNlr.mbRel = bRel; + } + } + // seek to end of additional data for this token + rStrm.seek( nEndPos ); + swapStreamPosition( rStrm ); + + return bValid; +} + +// convert BIFF token and push API operand or operator ------------------------ + +bool BiffFormulaParserImpl::pushBiffReference( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + return (mnCurrRefId > 0) ? + pushReferenceOperand( getExternalLinks().getSheetRange( mnCurrRefId, 0, 0 ), rRef, bDeleted, bRelativeAsOffset ) : + pushReferenceOperand( rRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::pushBiffReference( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + return (mnCurrRefId > 0) ? + pushReferenceOperand( getExternalLinks().getSheetRange( mnCurrRefId, 0, 0 ), rRef, bDeleted, bRelativeAsOffset ) : + pushReferenceOperand( rRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::pushBiffNlrAddr( const BiffNlr& rNlr, bool bRow ) +{ + BinSingleRef2d aRef; + aRef.mnCol = rNlr.mnCol; + aRef.mnRow = rNlr.mnRow; + aRef.mbColRel = !bRow; + aRef.mbRowRel = bRow; + return pushNlrOperand( aRef ); +} + +bool BiffFormulaParserImpl::pushBiffNlrRange( const BiffNlr& rNlr, const BinRange& rRange ) +{ + bool bRow = rNlr.mnRow == rRange.maFirst.mnRow; + return lclIsValidNlrRange( rNlr, rRange, bRow ) ? + pushBiffNlrAddr( rNlr, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::pushBiffNlrSAddr( const BiffNlr& rNlr, bool bRow ) +{ + BinRange aRange; + aRange.maFirst.mnCol = rNlr.mnCol + (bRow ? 1 : 0); + aRange.maFirst.mnRow = rNlr.mnRow + (bRow ? 0 : 1); + aRange.maLast.mnCol = bRow ? mnMaxApiCol : rNlr.mnCol; + aRange.maLast.mnRow = bRow ? rNlr.mnRow : mnMaxApiRow; + return pushBiffNlrSRange( rNlr, aRange, bRow ); +} + +bool BiffFormulaParserImpl::pushBiffNlrSRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ) +{ + if( lclIsValidNlrRange( rNlr, rRange, bRow ) ) + { + BinComplexRef2d aRef; + aRef.maRef1.mnCol = rRange.maFirst.mnCol; + aRef.maRef1.mnRow = rRange.maFirst.mnRow; + aRef.maRef2.mnCol = rRange.maLast.mnCol; + aRef.maRef2.mnRow = rRange.maLast.mnRow; + aRef.maRef1.mbColRel = aRef.maRef2.mbColRel = !bRow && rNlr.mbRel; + aRef.maRef1.mbRowRel = aRef.maRef2.mbRowRel = bRow && rNlr.mbRel; + return pushReferenceOperand( aRef, false, false ); + } + return pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::pushBiffName( sal_uInt16 nNameId ) +{ + // one-based in BIFF formulas + return pushDefinedNameOperand( getDefinedNames().getByIndex( static_cast< sal_Int32 >( nNameId ) - 1 ) ); +} + +bool BiffFormulaParserImpl::pushBiffExtName( sal_Int32 nRefId, sal_uInt16 nNameId ) +{ + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + { + if( pExtLink->getLinkType() == LINKTYPE_SELF ) + return pushBiffName( nNameId ); + // external name indexes are one-based in BIFF + ExternalNameRef xExtName = pExtLink->getNameByIndex( static_cast< sal_Int32 >( nNameId ) - 1 ); + return pushExternalNameOperand( xExtName, *pExtLink ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool BiffFormulaParserImpl::pushBiffFunction( sal_uInt16 nFuncId ) +{ + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiffFuncId( nFuncId ) ) + if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount ) + return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount ); + return pushFunctionOperator( OPCODE_NONAME, 0 ); +} + +bool BiffFormulaParserImpl::pushBiffFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ) +{ + if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) ) + nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK; + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiffFuncId( nFuncId ) ) + return pushFunctionOperator( *pFuncInfo, nParamCount ); + return pushFunctionOperator( OPCODE_NONAME, nParamCount ); +} + +// ============================================================================ + +namespace { + +/** Extracts the reference identifier and the remaining data from a formula in + the format '[RefID]Remaining'. */ +bool lclExtractRefId( sal_Int32& rnRefId, OUString& rRemainder, const OUString& rFormulaString ) +{ + if( (rFormulaString.getLength() >= 4) && (rFormulaString[ 0 ] == '[') ) + { + sal_Int32 nBracketClose = rFormulaString.indexOf( ']', 1 ); + if( nBracketClose >= 2 ) + { + rnRefId = rFormulaString.copy( 1, nBracketClose - 1 ).toInt32(); + rRemainder = rFormulaString.copy( nBracketClose + 1 ); + return !rRemainder.isEmpty(); + } + } + return false; +} + +} + +// ---------------------------------------------------------------------------- + +FormulaParser::FormulaParser( const WorkbookHelper& rHelper ) : + FormulaProcessorBase( rHelper ) +{ + switch( getFilterType() ) + { + case FILTER_OOXML: mxImpl.reset( new OoxFormulaParserImpl( *this ) ); break; + case FILTER_BIFF: mxImpl.reset( new BiffFormulaParserImpl( *this ) ); break; + case FILTER_UNKNOWN: break; + } +} + +FormulaParser::~FormulaParser() +{ +} + +ApiTokenSequence FormulaParser::importFormula( const CellAddress& rBaseAddress, const OUString& rFormulaString ) const +{ + return mxImpl->importOoxFormula( rBaseAddress, rFormulaString ); +} + +ApiTokenSequence FormulaParser::importFormula( const CellAddress& rBaseAddress, FormulaType eType, SequenceInputStream& rStrm ) const +{ + return mxImpl->importBiff12Formula( rBaseAddress, eType, rStrm ); +} + +ApiTokenSequence FormulaParser::importFormula( const CellAddress& rBaseAddress, FormulaType eType, BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ) const +{ + return mxImpl->importBiffFormula( rBaseAddress, eType, rStrm, pnFmlaSize ); +} + +ApiTokenSequence FormulaParser::convertBoolToFormula( bool bValue ) const +{ + if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiffFuncId( bValue ? BIFF_FUNC_TRUE : BIFF_FUNC_FALSE ) ) + { + ApiTokenSequence aTokens( 3 ); + aTokens[ 0 ].OpCode = pFuncInfo->mnApiOpCode; + aTokens[ 1 ].OpCode = OPCODE_OPEN; + aTokens[ 2 ].OpCode = OPCODE_CLOSE; + return aTokens; + } + return ApiTokenSequence(); +} + +ApiTokenSequence FormulaParser::convertErrorToFormula( sal_uInt8 nErrorCode ) const +{ + ApiTokenSequence aTokens( 3 ); + // HACK: enclose all error codes into an 1x1 matrix + aTokens[ 0 ].OpCode = OPCODE_ARRAY_OPEN; + aTokens[ 1 ].OpCode = OPCODE_PUSH; + aTokens[ 1 ].Data <<= BiffHelper::calcDoubleFromError( nErrorCode ); + aTokens[ 2 ].OpCode = OPCODE_ARRAY_CLOSE; + return aTokens; +} + +ApiTokenSequence FormulaParser::convertNameToFormula( sal_Int32 nTokenIndex ) const +{ + if( nTokenIndex < 0 ) + return convertErrorToFormula( BIFF_ERR_REF ); + + ApiTokenSequence aTokens( 1 ); + aTokens[ 0 ].OpCode = OPCODE_NAME; + NameToken aNameTokenData; + aNameTokenData.Global = sal_True; + aNameTokenData.Index = nTokenIndex; + aTokens[ 0 ].Data <<= aNameTokenData; + return aTokens; +} + +OUString FormulaParser::importOleTargetLink( const OUString& rFormulaString ) +{ + sal_Int32 nRefId = -1; + OUString aRemainder; + if( lclExtractRefId( nRefId, aRemainder, rFormulaString ) && (aRemainder.getLength() >= 3) && + (aRemainder[ 0 ] == '!') && (aRemainder[ 1 ] == '\'') && (aRemainder[ aRemainder.getLength() - 1 ] == '\'') ) + return mxImpl->resolveOleTarget( nRefId, false ); + return OUString(); +} + +OUString FormulaParser::importOleTargetLink( SequenceInputStream& rStrm ) +{ + OUString aTargetLink; + sal_Int32 nFmlaSize = rStrm.readInt32(); + sal_Int64 nFmlaEndPos = rStrm.tell() + ::std::max< sal_Int32 >( nFmlaSize, 0 ); + if( (nFmlaSize == 7) && (rStrm.getRemaining() >= 7) ) + { + sal_uInt8 nToken; + sal_Int16 nRefId; + sal_Int32 nNameId; + rStrm >> nToken >> nRefId >> nNameId; + if( nToken == (BIFF_TOKCLASS_VAL|BIFF_TOKID_NAMEX) ) + aTargetLink = mxImpl->resolveOleTarget( nRefId, true ); + } + rStrm.seek( nFmlaEndPos ); + return aTargetLink; +} + +OUString FormulaParser::importMacroName( const OUString& rFormulaString ) +{ + /* Valid macros are either sheet macros or VBA macros. OOXML and all BIFF + documents store defined names for sheet macros, but OOXML documents do + not store any defined name for VBA macros (while BIFF documents do). + Sheet macros may be defined locally to a sheet, or globally to the + document. As a result, all of the following macro specifiers are valid: + + 1) Macros located in the own document: + [0]!MySheetMacro (global sheet macro 'MySheetMacro') + Macro1!MyMacro (sheet-local sheet macro 'MyMacro') + [0]!MyVBAProc (VBA macro 'MyVBAProc') + [0]!Mod1.MyVBAProc (VBA macro 'MyVBAProc' from code module 'Mod1') + + 2) Macros from an external document: + [2]!MySheetMacro (global external sheet macro 'MySheetMacro') + [2]Macro1!MyMacro (sheet-local external sheet macro 'MyMacro') + [2]!MyVBAProc (external VBA macro 'MyVBAProc') + [2]!Mod1.MyVBAProc (external VBA macro from code module 'Mod1') + + This implementation is only interested in VBA macros from the own + document, ignoring the valid syntax 'Macro1!MyMacro' for sheet-local + sheet macros. + */ + sal_Int32 nRefId = -1; + OUString aRemainder; + if( lclExtractRefId( nRefId, aRemainder, rFormulaString ) && (aRemainder.getLength() > 1) && (aRemainder[ 0 ] == '!') ) + { + /* In BIFF12 documents, the reference identifier is always the + one-based index of the external link as it is in OOXML documents + (it is not an index into the list of reference sheets as used in + cell formulas). Index 0 is an implicit placeholder for the own + document. In BIFF12 documents, the reference to the own document is + stored explicitly, mostly at the top of the list, so index 1 may + resolve to the own document too. + Passing 'false' to getExternalLink() specifies to ignore the + reference sheets list (if existing) and to access the list of + external links directly. */ + const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, false ).get(); + OSL_ENSURE( pExtLink, "FormulaParser::importMacroName - missing link" ); + // do not accept macros in external documents (not supported) + if( pExtLink && (pExtLink->getLinkType() == LINKTYPE_SELF) ) + { + // ignore sheet macros (defined name for VBA macros may not exist, see above) + OUString aMacroName = aRemainder.copy( 1 ); + const DefinedName* pDefName = getDefinedNames().getByModelName( aMacroName ).get(); + if( !pDefName || pDefName->isVBName() ) + return aMacroName; + } + } + return OUString(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit