diff options
author | Jens-Heiner Rechtien <hr@openoffice.org> | 2009-07-29 14:46:52 +0000 |
---|---|---|
committer | Jens-Heiner Rechtien <hr@openoffice.org> | 2009-07-29 14:46:52 +0000 |
commit | 6488a6aeb9a5c5ed6cd18f37d29b91ef992588e5 (patch) | |
tree | 7ed5eca7066b647e0bb36578aa86696104e452d5 /oox/source | |
parent | e443a6d930c52ab7555a2ecbd719a8b32929f379 (diff) |
CWS-TOOLING: integrate CWS dr71
2009-07-07 16:26:00 +0200 dr r273805 : #i10000# unused variables
2009-07-07 10:27:14 +0200 dr r273780 : CWS-TOOLING: rebase CWS dr71 to trunk@273468 (milestone: DEV300:m51)
2009-07-01 11:28:24 +0200 dr r273559 : #101471# special handling for XL library functions in ODF formulas (EUROCONVERT)
2009-06-29 17:48:46 +0200 dr r273478 : #i101471# typo
2009-06-29 17:35:16 +0200 dr r273477 : #i101471# import msoxl: formulas from conditional formatting and data validation
2009-06-18 13:45:17 +0200 dr r273115 : #101471# changed interface css.sheet.XFormulaParser
2009-06-18 13:44:43 +0200 dr r273114 : #101471# changed interface css.sheet.XFormulaParser
2009-06-17 17:29:23 +0200 dr r273089 : #i101471# extend the XFormulaParser interface with a ReferencePosition parameter, make rel-refs from msoxl: namespace working
2009-06-17 17:28:39 +0200 dr r273088 : #i101471# extend the XFormulaParser interface with a ReferencePosition parameter
2009-06-17 17:28:19 +0200 dr r273087 : #i101471# extend the XFormulaParser interface with a ReferencePosition parameter
2009-06-17 17:27:19 +0200 dr r273086 : #i101471# extend the XFormulaParser interface with a ReferencePosition parameter, remove that property from FormulaParser service
2009-06-17 12:52:20 +0200 dr r273059 : #i101471# import cell formulas from msoxl: namespace
2009-06-16 11:40:50 +0200 dr r273013 : #i101471# import formula namespace from xml elements
2009-06-12 18:34:13 +0200 dr r272935 : #i101471# external formula parser for oox in odf
2009-06-12 18:33:13 +0200 dr r272934 : #i101471# external formula parsers
2009-06-12 18:29:46 +0200 dr r272933 : #i101471# external formula parsers
2009-06-05 15:53:47 +0200 dr r272705 : #i101471# provide OOX formula parser as UNO service
Diffstat (limited to 'oox/source')
-rw-r--r-- | oox/source/core/facreg.cxx | 4 | ||||
-rw-r--r-- | oox/source/token/properties.txt | 1 | ||||
-rw-r--r-- | oox/source/xls/externallinkbuffer.cxx | 5 | ||||
-rw-r--r-- | oox/source/xls/formulabase.cxx | 367 | ||||
-rw-r--r-- | oox/source/xls/formulaparser.cxx | 778 | ||||
-rw-r--r-- | oox/source/xls/makefile.mk | 1 | ||||
-rw-r--r-- | oox/source/xls/ooxformulaparser.cxx | 228 |
7 files changed, 859 insertions, 525 deletions
diff --git a/oox/source/core/facreg.cxx b/oox/source/core/facreg.cxx index 17489e27e11e..5ffd585e47ff 100644 --- a/oox/source/core/facreg.cxx +++ b/oox/source/core/facreg.cxx @@ -66,6 +66,7 @@ namespace oox { namespace shape { SERVICE( ShapeContextHandler ); } namespace shape { SERVICE( FastTokenHandlerService ); } namespace docprop { SERVICE2( OOXMLDocPropImportImpl ); } + namespace xls { SERVICE2( OOXMLFormulaParser ); } } // @@ -108,6 +109,7 @@ OOX_DLLPUBLIC sal_Bool SAL_CALL component_writeInfo( void * , void * pRegistryKe WRITEINFO( ::oox::shape::ShapeContextHandler ); WRITEINFO( ::oox::shape::FastTokenHandlerService ); WRITEINFO( ::oox::docprop::OOXMLDocPropImportImpl ); + WRITEINFO( ::oox::xls::OOXMLFormulaParser ); } catch (registry::InvalidRegistryException &) { @@ -147,7 +149,6 @@ OOX_DLLPUBLIC void * SAL_CALL component_getFactory( const sal_Char * pImplName, const sal_Int32 nImplNameLen = strlen( pImplName ); - // impress oasis import SINGLEFACTORY( ::oox::core::FilterDetect ) else SINGLEFACTORY( oox::ppt::PowerPointImport ) else SINGLEFACTORY( ::oox::xls::BiffDetector ) @@ -156,6 +157,7 @@ OOX_DLLPUBLIC void * SAL_CALL component_getFactory( const sal_Char * pImplName, else SINGLEFACTORY( ::oox::shape::ShapeContextHandler) else SINGLEFACTORY( ::oox::shape::FastTokenHandlerService) else SINGLEFACTORY2( ::oox::docprop::OOXMLDocPropImportImpl ) + else SINGLEFACTORY2( ::oox::xls::OOXMLFormulaParser ) if( xFactory.is()) { diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt index 84c6f547c04c..39cc9cfe58c0 100644 --- a/oox/source/token/properties.txt +++ b/oox/source/token/properties.txt @@ -288,7 +288,6 @@ Printable Protected Reference ReferenceDevice -ReferencePosition RegularExpressions RelId RelativeHorizontalTabbarWidth diff --git a/oox/source/xls/externallinkbuffer.cxx b/oox/source/xls/externallinkbuffer.cxx index 8bc5e2f48745..564775228d2a 100644 --- a/oox/source/xls/externallinkbuffer.cxx +++ b/oox/source/xls/externallinkbuffer.cxx @@ -750,15 +750,14 @@ void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl, const OUStr meLinkType = LINKTYPE_UNKNOWN; if( rTargetType == OOX_TARGETTYPE_EXTLINK ) { - maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl ); + maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl ); if( maTargetUrl.getLength() > 0 ) meLinkType = LINKTYPE_EXTERNAL; } else if( rTargetType == OOX_TARGETTYPE_LIBRARY ) { meLinkType = LINKTYPE_LIBRARY; - if( rTargetUrl.equalsIgnoreAsciiCaseAscii( "EUROTOOL.XLA" ) || rTargetUrl.equalsIgnoreAsciiCaseAscii( "EUROTOOL.XLAM" ) ) - meFuncLibType = FUNCLIB_EUROTOOL; + meFuncLibType = getFormulaParser().getFuncLibTypeFromLibraryName( rTargetUrl ); } OSL_ENSURE( meLinkType != LINKTYPE_UNKNOWN, "ExternalLink::setExternalTargetUrl - empty target URL or unknown target type" ); diff --git a/oox/source/xls/formulabase.cxx b/oox/source/xls/formulabase.cxx index 069dd75cce82..00da5cd2edb1 100644 --- a/oox/source/xls/formulabase.cxx +++ b/oox/source/xls/formulabase.cxx @@ -34,6 +34,7 @@ #include <rtl/ustrbuf.hxx> #include <com/sun/star/lang/XMultiServiceFactory.hpp> #include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/sheet/AddressConvention.hpp> #include <com/sun/star/sheet/ReferenceFlags.hpp> #include <com/sun/star/sheet/SingleReference.hpp> #include <com/sun/star/sheet/ComplexReference.hpp> @@ -41,9 +42,9 @@ #include <com/sun/star/sheet/FormulaMapGroup.hpp> #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp> #include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp> +#include <com/sun/star/sheet/XFormulaParser.hpp> #include <com/sun/star/sheet/XFormulaTokens.hpp> #include "properties.hxx" -#include "oox/helper/propertyset.hxx" #include "oox/helper/recordinputstream.hxx" #include "oox/core/filterbase.hxx" #include "oox/xls/biffinputstream.hxx" @@ -58,6 +59,7 @@ using ::com::sun::star::uno::Any; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::table::CellAddress; using ::com::sun::star::table::CellRangeAddress; @@ -294,9 +296,9 @@ static const FunctionData saFuncTableBiff2[] = { "SIN", "SIN", 15, 15, 1, 1, V, { V }, 0 }, { "COS", "COS", 16, 16, 1, 1, V, { V }, 0 }, { "TAN", "TAN", 17, 17, 1, 1, V, { V }, 0 }, - { "COT", "TAN", 17, 17, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "COT", 0, 17, 17, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, { "ATAN", "ATAN", 18, 18, 1, 1, V, { V }, 0 }, - { "ACOT", "ATAN", 18, 18, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "ACOT", 0, 18, 18, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, { "PI", "PI", 19, 19, 0, 0, V, {}, 0 }, { "SQRT", "SQRT", 20, 20, 1, 1, V, { V }, 0 }, { "EXP", "EXP", 21, 21, 1, 1, V, { V }, 0 }, @@ -457,11 +459,11 @@ static const FunctionData saFuncTableBiff3[] = { "SINH", "SINH", 229, 229, 1, 1, V, { V }, 0 }, { "COSH", "COSH", 230, 230, 1, 1, V, { V }, 0 }, { "TANH", "TANH", 231, 231, 1, 1, V, { V }, 0 }, - { "COTH", "TANH", 231, 231, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "COTH", 0, 231, 231, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, { "ASINH", "ASINH", 232, 232, 1, 1, V, { V }, 0 }, { "ACOSH", "ACOSH", 233, 233, 1, 1, V, { V }, 0 }, { "ATANH", "ATANH", 234, 234, 1, 1, V, { V }, 0 }, - { "ACOTH", "ATANH", 234, 234, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "ACOTH", 0, 234, 234, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, { "DGET", "DGET", 235, 235, 3, 3, V, { R }, 0 }, { "INFO", "INFO", 244, 244, 1, 1, V, { V }, FUNCFLAG_VOLATILE } }; @@ -790,15 +792,35 @@ FuncInfoParamClassIterator& FuncInfoParamClassIterator::operator++() // function provider ========================================================== -FunctionProvider::FunctionProvider( FilterType eFilter, BiffType eBiff, bool bImportFilter ) : - mxFuncs( new FuncVector ), - mxOdfFuncs( new FuncNameMap ), - mxOoxFuncs( new FuncNameMap ), - mxOobFuncs( new FuncIdMap ), - mxBiffFuncs( new FuncIdMap ), - mxMacroFuncs( new FuncNameMap ) -{ - OSL_ENSURE( bImportFilter, "FunctionProvider::FunctionProvider - need special handling for macro call functions" ); +struct FunctionProviderImpl +{ + typedef RefMap< OUString, FunctionInfo > FuncNameMap; + typedef RefMap< sal_uInt16, FunctionInfo > FuncIdMap; + + FunctionInfoVector maFuncs; /// All function infos in one list. + FuncNameMap maOdfFuncs; /// Maps ODF function names to function data. + FuncNameMap maOoxFuncs; /// Maps OOXML function names to function data. + FuncIdMap maOobFuncs; /// Maps OOBIN function indexes to function data. + FuncIdMap maBiffFuncs; /// Maps BIFF function indexes to function data. + FuncNameMap maMacroFuncs; /// Maps macro function names to function data. + + explicit FunctionProviderImpl( FilterType eFilter, BiffType eBiff, bool bImportFilter ); + +private: + /** Creates and inserts a function info struct from the passed function data. */ + void initFunc( const FunctionData& rFuncData, sal_uInt8 nMaxParam ); + + /** Initializes the members from the passed function data list. */ + void initFuncs( + const FunctionData* pBeg, const FunctionData* pEnd, + sal_uInt8 nMaxParam, bool bImportFilter ); +}; + +// ---------------------------------------------------------------------------- + +FunctionProviderImpl::FunctionProviderImpl( FilterType eFilter, BiffType eBiff, bool bImportFilter ) +{ + OSL_ENSURE( bImportFilter, "FunctionProviderImpl::FunctionProviderImpl - need special handling for macro call functions" ); sal_uInt8 nMaxParam = 0; switch( eFilter ) { @@ -810,10 +832,10 @@ FunctionProvider::FunctionProvider( FilterType eFilter, BiffType eBiff, bool bIm nMaxParam = BIFF_MAX_PARAMCOUNT; break; case FILTER_UNKNOWN: - OSL_ENSURE( false, "FunctionProvider::FunctionProvider - invalid filter type" ); + OSL_ENSURE( false, "FunctionProviderImpl::FunctionProviderImpl - invalid filter type" ); break; } - OSL_ENSURE( eBiff != BIFF_UNKNOWN, "FunctionProvider::FunctionProvider - invalid BIFF type" ); + OSL_ENSURE( eBiff != BIFF_UNKNOWN, "FunctionProviderImpl::FunctionProviderImpl - invalid BIFF type" ); /* Add functions supported in the current BIFF version only. Function tables from later BIFF versions may overwrite single functions from @@ -832,38 +854,7 @@ FunctionProvider::FunctionProvider( FilterType eFilter, BiffType eBiff, bool bIm initFuncs( saFuncTableOox, STATIC_ARRAY_END( saFuncTableOox ), nMaxParam, bImportFilter ); } -FunctionProvider::~FunctionProvider() -{ -} - -const FunctionInfo* FunctionProvider::getFuncInfoFromOdfFuncName( const OUString& rFuncName ) const -{ - return mxOdfFuncs->get( rFuncName ).get(); -} - -const FunctionInfo* FunctionProvider::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const -{ - return mxOoxFuncs->get( rFuncName ).get(); -} - -const FunctionInfo* FunctionProvider::getFuncInfoFromOobFuncId( sal_uInt16 nFuncId ) const -{ - return mxOobFuncs->get( nFuncId ).get(); -} - -const FunctionInfo* FunctionProvider::getFuncInfoFromBiffFuncId( sal_uInt16 nFuncId ) const -{ - return mxBiffFuncs->get( nFuncId ).get(); -} - -const FunctionInfo* FunctionProvider::getFuncInfoFromMacroName( const OUString& rFuncName ) const -{ - return mxMacroFuncs->get( rFuncName ).get(); -} - -// private -------------------------------------------------------------------- - -void FunctionProvider::initFunc( const FunctionData& rFuncData, sal_uInt8 nMaxParam ) +void FunctionProviderImpl::initFunc( const FunctionData& rFuncData, sal_uInt8 nMaxParam ) { // create a function info object FunctionInfoRef xFuncInfo( new FunctionInfo ); @@ -895,38 +886,122 @@ void FunctionProvider::initFunc( const FunctionData& rFuncData, sal_uInt8 nMaxPa setFlag( xFuncInfo->mnBiffFuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd ); // insert the function info into the member maps - mxFuncs->push_back( xFuncInfo ); + maFuncs.push_back( xFuncInfo ); if( xFuncInfo->maOdfFuncName.getLength() > 0 ) - (*mxOdfFuncs)[ xFuncInfo->maOdfFuncName ] = xFuncInfo; + maOdfFuncs[ xFuncInfo->maOdfFuncName ] = xFuncInfo; if( xFuncInfo->maOoxFuncName.getLength() > 0 ) - (*mxOoxFuncs)[ xFuncInfo->maOoxFuncName ] = xFuncInfo; + maOoxFuncs[ xFuncInfo->maOoxFuncName ] = xFuncInfo; if( xFuncInfo->mnOobFuncId != NOID ) - (*mxOobFuncs)[ xFuncInfo->mnOobFuncId ] = xFuncInfo; + maOobFuncs[ xFuncInfo->mnOobFuncId ] = xFuncInfo; if( xFuncInfo->mnBiffFuncId != NOID ) - (*mxBiffFuncs)[ xFuncInfo->mnBiffFuncId ] = xFuncInfo; + maBiffFuncs[ xFuncInfo->mnBiffFuncId ] = xFuncInfo; if( xFuncInfo->maBiffMacroName.getLength() > 0 ) - (*mxMacroFuncs)[ xFuncInfo->maBiffMacroName ] = xFuncInfo; + maMacroFuncs[ xFuncInfo->maBiffMacroName ] = xFuncInfo; } -void FunctionProvider::initFuncs( const FunctionData* pBeg, const FunctionData* pEnd, sal_uInt8 nMaxParam, bool bImportFilter ) +void FunctionProviderImpl::initFuncs( const FunctionData* pBeg, const FunctionData* pEnd, sal_uInt8 nMaxParam, bool bImportFilter ) { for( const FunctionData* pIt = pBeg; pIt != pEnd; ++pIt ) if( pIt->isSupported( bImportFilter ) ) initFunc( *pIt, nMaxParam ); } +// ---------------------------------------------------------------------------- + +FunctionProvider::FunctionProvider( FilterType eFilter, BiffType eBiff, bool bImportFilter ) : + mxFuncImpl( new FunctionProviderImpl( eFilter, eBiff, bImportFilter ) ) +{ +} + +FunctionProvider::~FunctionProvider() +{ +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromOdfFuncName( const OUString& rFuncName ) const +{ + return mxFuncImpl->maOdfFuncs.get( rFuncName ).get(); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const +{ + return mxFuncImpl->maOoxFuncs.get( rFuncName ).get(); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromOobFuncId( sal_uInt16 nFuncId ) const +{ + return mxFuncImpl->maOobFuncs.get( nFuncId ).get(); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromBiffFuncId( sal_uInt16 nFuncId ) const +{ + return mxFuncImpl->maBiffFuncs.get( nFuncId ).get(); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromMacroName( const OUString& rFuncName ) const +{ + return mxFuncImpl->maMacroFuncs.get( rFuncName ).get(); +} + +FunctionLibraryType FunctionProvider::getFuncLibTypeFromLibraryName( const OUString& rLibraryName ) const +{ +#define OOX_XLS_IS_LIBNAME( libname, basename ) (libname.equalsIgnoreAsciiCaseAscii( basename ".XLA" ) || libname.equalsIgnoreAsciiCaseAscii( basename ".XLAM" )) + + // the EUROTOOL add-in containing the EUROCONVERT function + if( OOX_XLS_IS_LIBNAME( rLibraryName, "EUROTOOL" ) ) + return FUNCLIB_EUROTOOL; + +#undef OOX_XLS_IS_LIBNAME + + // default: unknown library + return FUNCLIB_UNKNOWN; +} + +const FunctionInfoVector& FunctionProvider::getFuncs() const +{ + return mxFuncImpl->maFuncs; +} + // op-code and function provider ============================================== -OpCodeProvider::OpCodeProvider( const WorkbookHelper& rHelper ) : - FunctionProvider( rHelper.getFilterType(), rHelper.getBiff(), rHelper.getBaseFilter().isImportFilter() ), - WorkbookHelper( rHelper ), - mxOpCodeFuncs( new OpCodeFuncMap ), - mxExtProgFuncs( new FuncNameMap ), - mxParserMap( new OpCodeEntryVector ) +struct OpCodeProviderImpl : public ApiOpCodes { - try + typedef RefMap< sal_Int32, FunctionInfo > OpCodeFuncMap; + typedef RefMap< OUString, FunctionInfo > FuncNameMap; + typedef ::std::vector< FormulaOpCodeMapEntry > OpCodeEntryVector; + + OpCodeFuncMap maOpCodeFuncs; /// Maps API function op-codes to function data. + FuncNameMap maExtProgFuncs; /// Maps programmatical API function names to function data. + OpCodeEntryVector maParserMap; /// OOXML token mapping for formula parser service. + + explicit OpCodeProviderImpl( + const FunctionInfoVector& rFuncInfos, + const Reference< XMultiServiceFactory >& rxFactory ); + +private: + typedef ::std::map< OUString, ApiToken > ApiTokenMap; + typedef Sequence< FormulaOpCodeMapEntry > OpCodeEntrySequence; + + static bool fillEntrySeq( OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ); + static bool fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ); + bool fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const; + + static bool initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId ); + bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName ); + bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const sal_Char* pcOdfName, const sal_Char* pcOoxName ); + bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName ); + + bool initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap ); + bool initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos ); +}; + +// ---------------------------------------------------------------------------- + +OpCodeProviderImpl::OpCodeProviderImpl( const FunctionInfoVector& rFuncInfos, + const Reference< XMultiServiceFactory >& rxFactory ) +{ + if( rxFactory.is() ) try { - Reference< XFormulaOpCodeMapper > xMapper( getDocumentFactory()->createInstance( + Reference< XFormulaOpCodeMapper > xMapper( rxFactory->createInstance( CREATE_OUSTRING( "com.sun.star.sheet.FormulaOpCodeMapper" ) ), UNO_QUERY_THROW ); // op-codes provided as attributes @@ -985,61 +1060,22 @@ OpCodeProvider::OpCodeProvider( const WorkbookHelper& rHelper ) : initOpCode( OPCODE_RANGE, aTokenMap, ':', ':' ) && // functions fillFuncTokenMaps( aTokenMap, aExtFuncTokenMap, aEntrySeq, xMapper ) && - initFuncOpCodes( aTokenMap, aExtFuncTokenMap ) && + initFuncOpCodes( aTokenMap, aExtFuncTokenMap, rFuncInfos ) && initOpCode( OPCODE_DDE, aTokenMap, "DDE", 0 ); - OSL_ENSURE( bIsValid, "OpCodeProvider::OpCodeProvider - opcodes not initialized" ); + OSL_ENSURE( bIsValid, "OpCodeProviderImpl::OpCodeProviderImpl - opcodes not initialized" ); (void)bIsValid; // OPCODE_PLUS_SIGN and OPCODE_ADD should be equal, otherwise "+" has to be passed above - OSL_ENSURE( OPCODE_PLUS_SIGN == OPCODE_ADD, "OpCodeProvider::OpCodeProvider - need opcode mapping for OPCODE_PLUS_SIGN" ); + OSL_ENSURE( OPCODE_PLUS_SIGN == OPCODE_ADD, "OpCodeProviderImpl::OpCodeProviderImpl - need opcode mapping for OPCODE_PLUS_SIGN" ); } catch( Exception& ) { - OSL_ENSURE( false, "OpCodeProvider::OpCodeProvider - cannot receive formula opcode mapper" ); - } -} - -OpCodeProvider::~OpCodeProvider() -{ -} - -const FunctionInfo* OpCodeProvider::getFuncInfoFromApiToken( const ApiToken& rToken ) const -{ - const FunctionInfo* pFuncInfo = 0; - if( (rToken.OpCode == OPCODE_EXTERNAL) && rToken.Data.hasValue() ) - { - OUString aProgFuncName; - if( rToken.Data >>= aProgFuncName ) - pFuncInfo = mxExtProgFuncs->get( aProgFuncName ).get(); - } - else if( (rToken.OpCode == OPCODE_MACRO) && rToken.Data.hasValue() ) - { - OUString aMacroName; - if( rToken.Data >>= aMacroName ) - pFuncInfo = getFuncInfoFromMacroName( aMacroName ); - } - else if( (rToken.OpCode == OPCODE_BAD) && rToken.Data.hasValue() ) - { - OUString aOoxFuncName; - if( rToken.Data >>= aOoxFuncName ) - pFuncInfo = getFuncInfoFromOoxFuncName( aOoxFuncName ); - } - else - { - pFuncInfo = mxOpCodeFuncs->get( rToken.OpCode ).get(); + OSL_ENSURE( false, "OpCodeProviderImpl::OpCodeProviderImpl - cannot receive formula opcode mapper" ); } - return pFuncInfo; -} - -Sequence< FormulaOpCodeMapEntry > OpCodeProvider::getOoxParserMap() const -{ - return ContainerHelper::vectorToSequence( *mxParserMap ); } -// private -------------------------------------------------------------------- - -bool OpCodeProvider::fillEntrySeq( OpCodeEntrySequence& orEntrySeq, +bool OpCodeProviderImpl::fillEntrySeq( OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ) { try @@ -1053,7 +1089,7 @@ bool OpCodeProvider::fillEntrySeq( OpCodeEntrySequence& orEntrySeq, return false; } -bool OpCodeProvider::fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, +bool OpCodeProviderImpl::fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ) { orTokenMap.clear(); @@ -1067,7 +1103,7 @@ bool OpCodeProvider::fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& return orEntrySeq.hasElements(); } -bool OpCodeProvider::fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const +bool OpCodeProviderImpl::fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const { orIntFuncTokenMap.clear(); orExtFuncTokenMap.clear(); @@ -1081,7 +1117,7 @@ bool OpCodeProvider::fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiToken return orEntrySeq.hasElements(); } -bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId ) +bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId ) { if( (0 <= nSpecialId) && (nSpecialId < rEntrySeq.getLength()) ) { @@ -1089,12 +1125,12 @@ bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence return true; } OSL_ENSURE( false, - OStringBuffer( "OpCodeProvider::initOpCode - opcode for special offset " ). + OStringBuffer( "OpCodeProviderImpl::initOpCode - opcode for special offset " ). append( nSpecialId ).append( " not found" ).getStr() ); return false; } -bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName ) +bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName ) { ApiTokenMap::const_iterator aIt = rTokenMap.find( rOdfName ); if( aIt != rTokenMap.end() ) @@ -1105,32 +1141,32 @@ bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rToken FormulaOpCodeMapEntry aEntry; aEntry.Name = rOoxName; aEntry.Token.OpCode = ornOpCode; - mxParserMap->push_back( aEntry ); + maParserMap.push_back( aEntry ); } return true; } OSL_ENSURE( false, - OStringBuffer( "OpCodeProvider::initOpCode - opcode for \"" ). + OStringBuffer( "OpCodeProviderImpl::initOpCode - opcode for \"" ). append( OUStringToOString( rOdfName, RTL_TEXTENCODING_ASCII_US ) ). append( "\" not found" ).getStr() ); return false; } -bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const sal_Char* pcOdfName, const sal_Char* pcOoxName ) +bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const sal_Char* pcOdfName, const sal_Char* pcOoxName ) { OUString aOoxName; if( pcOoxName ) aOoxName = OUString::createFromAscii( pcOoxName ); return initOpCode( ornOpCode, rTokenMap, OUString::createFromAscii( pcOdfName ), aOoxName ); } -bool OpCodeProvider::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName ) +bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName ) { OUString aOoxName; if( cOoxName ) aOoxName = OUString( cOoxName ); return initOpCode( ornOpCode, rTokenMap, OUString( cOdfName ), aOoxName ); } -bool OpCodeProvider::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap ) +bool OpCodeProviderImpl::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap ) { bool bIsValid = false; if( orFuncInfo.maOdfFuncName.getLength() > 0 ) @@ -1142,10 +1178,9 @@ bool OpCodeProvider::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap bIsValid = (orFuncInfo.mnApiOpCode >= 0) && (orFuncInfo.mnApiOpCode != OPCODE_UNKNOWN) && - (orFuncInfo.mnApiOpCode != OPCODE_NONAME) && - (orFuncInfo.maOoxFuncName.getLength() > 0); + (orFuncInfo.mnApiOpCode != OPCODE_NONAME); OSL_ENSURE( bIsValid, - OStringBuffer( "OpCodeProvider::initFuncOpCode - no valid opcode or missing OOX function name for ODF function \"" ). + OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no valid opcode for ODF function \"" ). append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ). append( '"' ).getStr() ); @@ -1153,18 +1188,19 @@ bool OpCodeProvider::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap { bIsValid = (aIt->second.Data >>= orFuncInfo.maExtProgName) && (orFuncInfo.maExtProgName.getLength() > 0); OSL_ENSURE( bIsValid, - OStringBuffer( "OpCodeProvider::initFuncOpCode - no programmatical name for external function \"" ). + OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no programmatical name for external function \"" ). append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ). append( '"' ).getStr() ); } - if( bIsValid ) + // add to parser map, if OOX function name exists + if( bIsValid && (orFuncInfo.maOoxFuncName.getLength() > 0) ) { // create the parser map entry FormulaOpCodeMapEntry aEntry; aEntry.Name = orFuncInfo.maOoxFuncName; aEntry.Token = aIt->second; - mxParserMap->push_back( aEntry ); + maParserMap.push_back( aEntry ); } } } @@ -1184,10 +1220,10 @@ bool OpCodeProvider::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap return bIsValid; } -bool OpCodeProvider::initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap ) +bool OpCodeProviderImpl::initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos ) { bool bIsValid = true; - for( FuncVector::const_iterator aIt = getFuncs().begin(), aEnd = getFuncs().end(); aIt != aEnd; ++aIt ) + for( FunctionInfoVector::const_iterator aIt = rFuncInfos.begin(), aEnd = rFuncInfos.end(); aIt != aEnd; ++aIt ) { FunctionInfoRef xFuncInfo = *aIt; // set API opcode from ODF function name @@ -1196,14 +1232,85 @@ bool OpCodeProvider::initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const if( xFuncInfo->mnApiOpCode != OPCODE_NONAME ) { if( (xFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && (xFuncInfo->maExtProgName.getLength() > 0) ) - (*mxExtProgFuncs)[ xFuncInfo->maExtProgName ] = xFuncInfo; + maExtProgFuncs[ xFuncInfo->maExtProgName ] = xFuncInfo; else - (*mxOpCodeFuncs)[ xFuncInfo->mnApiOpCode ] = xFuncInfo; + maOpCodeFuncs[ xFuncInfo->mnApiOpCode ] = xFuncInfo; } } return bIsValid; } +// ---------------------------------------------------------------------------- + +OpCodeProvider::OpCodeProvider( const Reference< XMultiServiceFactory >& rxFactory, + FilterType eFilter, BiffType eBiff, bool bImportFilter ) : + FunctionProvider( eFilter, eBiff, bImportFilter ), + mxOpCodeImpl( new OpCodeProviderImpl( getFuncs(), rxFactory ) ) +{ +} + +OpCodeProvider::~OpCodeProvider() +{ +} + +const ApiOpCodes& OpCodeProvider::getOpCodes() const +{ + return *mxOpCodeImpl; +} + +const FunctionInfo* OpCodeProvider::getFuncInfoFromApiToken( const ApiToken& rToken ) const +{ + const FunctionInfo* pFuncInfo = 0; + if( (rToken.OpCode == mxOpCodeImpl->OPCODE_EXTERNAL) && rToken.Data.has< OUString >() ) + pFuncInfo = mxOpCodeImpl->maExtProgFuncs.get( rToken.Data.get< OUString >() ).get(); + else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_MACRO) && rToken.Data.has< OUString >() ) + pFuncInfo = getFuncInfoFromMacroName( rToken.Data.get< OUString >() ); + else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_BAD) && rToken.Data.has< OUString >() ) + pFuncInfo = getFuncInfoFromOoxFuncName( rToken.Data.get< OUString >() ); + else + pFuncInfo = mxOpCodeImpl->maOpCodeFuncs.get( rToken.OpCode ).get(); + return pFuncInfo; +} + +Sequence< FormulaOpCodeMapEntry > OpCodeProvider::getOoxParserMap() const +{ + return ContainerHelper::vectorToSequence( mxOpCodeImpl->maParserMap ); +} + +// API formula parser wrapper ================================================= + +ApiParserWrapper::ApiParserWrapper( + const Reference< XMultiServiceFactory >& rxFactory, const OpCodeProvider& rOpCodeProv ) : + OpCodeProvider( rOpCodeProv ) +{ + if( rxFactory.is() ) try + { + mxParser.set( rxFactory->createInstance( CREATE_OUSTRING( "com.sun.star.sheet.FormulaParser" ) ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + OSL_ENSURE( mxParser.is(), "ApiParserWrapper::ApiParserWrapper - cannot create API formula parser object" ); + maParserProps.set( mxParser ); + maParserProps.setProperty( PROP_CompileEnglish, true ); + maParserProps.setProperty( PROP_FormulaConvention, ::com::sun::star::sheet::AddressConvention::XL_OOX ); + maParserProps.setProperty( PROP_IgnoreLeadingSpaces, false ); + maParserProps.setProperty( PROP_OpCodeMap, getOoxParserMap() ); +} + +ApiTokenSequence ApiParserWrapper::parseFormula( const OUString& rFormula, const CellAddress& rRefPos ) +{ + ApiTokenSequence aTokenSeq; + if( mxParser.is() ) try + { + aTokenSeq = mxParser->parseFormula( rFormula, rRefPos ); + } + catch( Exception& ) + { + } + return aTokenSeq; +} + // formula contexts =========================================================== FormulaContext::FormulaContext( bool bRelativeAsOffset, bool b2dRefsAs3dRefs, bool bAllowNulChars ) : @@ -1249,7 +1356,7 @@ void SimpleFormulaContext::setTokens( const ApiTokenSequence& rTokens ) mxTokens->setTokens( rTokens ); } -// formula parser/formula compiler base class ================================= +// formula parser/printer base class for filters ============================== namespace { @@ -1321,7 +1428,9 @@ TokenToRangeListState lclProcessClose( sal_Int32& ornParenLevel ) // ---------------------------------------------------------------------------- FormulaProcessorBase::FormulaProcessorBase( const WorkbookHelper& rHelper ) : - OpCodeProvider( rHelper ) + OpCodeProvider( rHelper.getDocumentFactory(), rHelper.getFilterType(), rHelper.getBiff(), rHelper.getBaseFilter().isImportFilter() ), + ApiOpCodes( getOpCodes() ), + WorkbookHelper( rHelper ) { } diff --git a/oox/source/xls/formulaparser.cxx b/oox/source/xls/formulaparser.cxx index 4183cb68c847..d2818ff9cb1a 100644 --- a/oox/source/xls/formulaparser.cxx +++ b/oox/source/xls/formulaparser.cxx @@ -30,16 +30,12 @@ #include "oox/xls/formulaparser.hxx" #include <com/sun/star/lang/XMultiServiceFactory.hpp> -#include <com/sun/star/sheet/AddressConvention.hpp> #include <com/sun/star/sheet/ComplexReference.hpp> #include <com/sun/star/sheet/ExternalReference.hpp> #include <com/sun/star/sheet/FormulaToken.hpp> #include <com/sun/star/sheet/ReferenceFlags.hpp> #include <com/sun/star/sheet/SingleReference.hpp> -#include <com/sun/star/sheet/XFormulaParser.hpp> #include "properties.hxx" -#include "oox/helper/containerhelper.hxx" -#include "oox/helper/propertyset.hxx" #include "oox/helper/recordinputstream.hxx" #include "oox/core/filterbase.hxx" #include "oox/xls/addressconverter.hxx" @@ -78,12 +74,351 @@ sal_uInt16 lclReadFmlaSize( BiffInputStream& rStrm, BiffType eBiff, const sal_uI } // 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.getLength() > 0) ) + 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.getLength() > 0) ) + orFuncToken.Data <<= pFuncInfo->maExtProgName; + // name of unsupported function, convert to OPCODE_BAD to preserve the name + else if( (pFuncInfo->mnApiOpCode == OPCODE_BAD) && (pFuncInfo->maOoxFuncName.getLength() > 0) ) + 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.getLength() > 0 ) + { + 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 + FuncInfoParamClassIterator aClassIt( *pRealFuncInfo ); + size_t nLastValidSize = maTokens.size(); + size_t nLastValidCount = 0; + for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aClassIt ) + { + // add embedded Calc-only parameters + if( aClassIt.isCalcOnlyParam() ) + { + appendCalcOnlyParameter( *pRealFuncInfo, nParam ); + while( aClassIt.isCalcOnlyParam() ) ++aClassIt; + } + + const ApiToken* pParamBegin = *aPosIt + 1; + const ApiToken* pParamEnd = *(aPosIt + 1); + bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd ); + + if( !aClassIt.isExcelOnlyParam() ) + { + // replace empty second and third parameter in IF function with zeros + if( (pRealFuncInfo->mnOobFuncId == OOBIN_FUNC_IF) && ((nParam == 1) || (nParam == 2)) && bIsEmpty ) + { + maTokens.append< double >( OPCODE_PUSH, 0.0 ); + bIsEmpty = false; + } + else + { + // 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( aClassIt.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::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ) +{ + (void)nParam; // prevent 'unused' warning + switch( rFuncInfo.mnOobFuncId ) + { + case OOBIN_FUNC_FLOOR: + case OOBIN_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.mnOobFuncId ) + { + case OOBIN_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 OpCodeProvider +class FormulaParserImpl : public FormulaFinalizer, public WorkbookHelper { public: - explicit FormulaParserImpl( const OpCodeProvider& rOpCodeProv ); + explicit FormulaParserImpl( const FormulaParser& rParent ); /** Converts an XML formula string. */ virtual void importOoxFormula( @@ -208,23 +543,11 @@ private: 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 ------------------------------------------------ - typedef ::std::vector< const ApiToken* > ParameterPosVector; - - void processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd ); - const ApiToken* processParameters( const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd ); - - const FunctionInfo* getFuncInfoFromLibFuncName( const ApiToken& rToken ) const; - bool isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; - const ApiToken* getExternCallToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; - const FunctionInfo* convertExternCallParam( ApiToken& orFuncToken, const ApiToken& rECToken ) const; - const ApiToken* skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; - const ApiToken* findParameters( ParameterPosVector& rParams, const ApiToken* pToken, const ApiToken* pTokenEnd ) const; - void appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ); - void appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount ); - - void appendFinalToken( const ApiToken& rToken ); + 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. @@ -246,12 +569,13 @@ private: // ---------------------------------------------------------------------------- -FormulaParserImpl::FormulaParserImpl( const OpCodeProvider& rOpCodeProv ) : - OpCodeProvider( rOpCodeProv ), - mnMaxApiCol( rOpCodeProv.getAddressConverter().getMaxApiAddress().Column ), - mnMaxApiRow( rOpCodeProv.getAddressConverter().getMaxApiAddress().Row ), - mnMaxXlsCol( rOpCodeProv.getAddressConverter().getMaxXlsAddress().Column ), - mnMaxXlsRow( rOpCodeProv.getAddressConverter().getMaxXlsAddress().Row ), +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 ), mpContext( 0 ) { // reserve enough space to make resize(), push_back() etc. cheap @@ -303,11 +627,9 @@ void FormulaParserImpl::initializeImport( FormulaContext& rContext ) void FormulaParserImpl::finalizeImport( const ApiTokenSequence& rTokens ) { - maTokenStorage.clear(); - const ApiToken* pToken = rTokens.getConstArray(); - processTokens( pToken, pToken + rTokens.getLength() ); - if( !maTokenStorage.empty() ) - mpContext->setTokens( ContainerHelper::vectorToSequence( maTokenStorage ) ); + ApiTokenSequence aFinalTokens = finalizeTokenArray( rTokens ); + if( aFinalTokens.hasElements() ) + mpContext->setTokens( aFinalTokens ); } void FormulaParserImpl::finalizeImport() @@ -885,342 +1207,34 @@ void FormulaParserImpl::convertReference3d( ComplexReference& orApiRef, const Li // finalize token sequence ---------------------------------------------------- -void FormulaParserImpl::processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd ) -{ - while( pToken < pTokenEnd ) - { - // push the current token into the vector - appendFinalToken( *pToken ); - const FunctionInfo* pFuncInfo; - // try to process a function - if( (pFuncInfo = getFuncInfoFromApiToken( *pToken )) != 0 ) - { - pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd ); - } - // try to process a function from an external library - else if( (pFuncInfo = getFuncInfoFromLibFuncName( *pToken )) != 0 ) - { - ApiToken& rFuncToken = maTokenStorage.back(); - rFuncToken.OpCode = pFuncInfo->mnApiOpCode; - if( (rFuncToken.OpCode == OPCODE_EXTERNAL) && (pFuncInfo->maExtProgName.getLength() > 0) ) - rFuncToken.Data <<= pFuncInfo->maExtProgName; - else - rFuncToken.Data.clear(); // clear string from OPCODE_BAD - pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd ); - } - // otherwise, go to next token - else - { - ++pToken; - } - } -} - -namespace { - -bool lclTokenHasChar( const ApiToken& rToken, sal_Int32 nOpCode, sal_Unicode cChar ) -{ - return (rToken.OpCode == nOpCode) && rToken.Data.has< OUString >() && (rToken.Data.get< OUString >() == OUString( cChar )); -} - -bool lclTokenHasDouble( const ApiToken& rToken, sal_Int32 nOpCode ) -{ - return (rToken.OpCode == nOpCode) && rToken.Data.has< double >(); -} - -} // namespace - -const ApiToken* FormulaParserImpl::processParameters( - const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd ) +const FunctionInfo* FormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const { - /* OOXML import of library functions pushes the external reference "[n]!" - as BAD/PUSH/BAD/BAD tokens in front of the function name. Try to find - and remove them here. TODO: This will change with CWS mooxlsc, there, - the reference ID and function name are passed together in a BAD token, - see getFuncInfoFromLibFuncName(). */ - if( (rFuncInfo.meFuncLibType != FUNCLIB_UNKNOWN) && (maTokenStorage.size() >= 5) ) + /* 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_Size nSize = maTokenStorage.size(); - if( lclTokenHasChar( maTokenStorage[ nSize - 5 ], OPCODE_BAD, '[' ) && - lclTokenHasDouble( maTokenStorage[ nSize - 4 ], OPCODE_PUSH ) && - lclTokenHasChar( maTokenStorage[ nSize - 3 ], OPCODE_BAD, ']' ) && - lclTokenHasChar( maTokenStorage[ nSize - 2 ], OPCODE_BAD, '!' ) ) + sal_Int32 nRefId = rTokenData.copy( nBracketOpen + 1, nBracketClose - nBracketOpen - 1 ).toInt32(); + const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get(); + if( pExtLink && (pExtLink->getLinkType() == LINKTYPE_LIBRARY) ) { - maTokenStorage.erase( maTokenStorage.end() - 5, maTokenStorage.end() - 1 ); - } - } - - // remember position of the token containing the function op-code - size_t nFuncNameIdx = maTokenStorage.size() - 1; - - // process a function, if an OPCODE_OPEN token is following - OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaParserImpl::processParameters - OPCODE_OPEN expected" ); - if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN) ) - { - // append the OPCODE_OPEN token to the vector - maTokenStorage.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, "FormulaParserImpl::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 add-ins, first parameter is reference to function name - if( rFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL ) - { - maTokenStorage[ nFuncNameIdx ].OpCode = OPCODE_NONAME; - // try to initialize function token from first parameter - if( const ApiToken* pECToken = getExternCallToken( *aPosIt + 1, *(aPosIt + 1) ) ) - if( const FunctionInfo* pECFuncInfo = convertExternCallParam( maTokenStorage[ nFuncNameIdx ], *pECToken ) ) - pRealFuncInfo = pECFuncInfo; - // on success, ignore first parameter - if( maTokenStorage[ nFuncNameIdx ].OpCode != OPCODE_NONAME ) - { - --nParamCount; - ++aPosIt; - } - } - - // process all parameters - FuncInfoParamClassIterator aClassIt( *pRealFuncInfo ); - size_t nLastValidSize = maTokenStorage.size(); - size_t nLastValidCount = 0; - for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aClassIt ) - { - // add embedded Calc-only parameters - if( aClassIt.isCalcOnlyParam() ) - { - appendCalcOnlyParameter( *pRealFuncInfo, nParam ); - while( aClassIt.isCalcOnlyParam() ) ++aClassIt; - } - - const ApiToken* pParamBegin = *aPosIt + 1; - const ApiToken* pParamEnd = *(aPosIt + 1); - bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd ); - - if( !aClassIt.isExcelOnlyParam() ) - { - // replace empty second and third parameter in IF function with zeros - if( (pRealFuncInfo->mnOobFuncId == OOBIN_FUNC_IF) && ((nParam == 1) || (nParam == 2)) && bIsEmpty ) - { - maTokenStorage.append< double >( OPCODE_PUSH, 0.0 ); - bIsEmpty = false; - } - else - { - // process all tokens of the parameter - processTokens( pParamBegin, pParamEnd ); - } - // append parameter separator token - maTokenStorage.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 = maTokenStorage.size(); - nLastValidCount = nParam + 1; - } - } - - // #84453# remove trailing optional empty parameters - maTokenStorage.resize( nLastValidSize ); - - // add trailing Calc-only parameters - if( aClassIt.isCalcOnlyParam() ) - appendCalcOnlyParameter( *pRealFuncInfo, nLastValidCount ); - - // add optional parameters that are required in Calc - appendRequiredParameters( *pRealFuncInfo, nLastValidCount ); - - // remove last parameter separator token - if( maTokenStorage.back().OpCode == OPCODE_SEP ) - maTokenStorage.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 ) - maTokenStorage.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 = maTokenStorage[ nFuncNameIdx ]; - if( (rFuncNameToken.OpCode == OPCODE_EXTERNAL) && !rFuncNameToken.Data.hasValue() ) - rFuncNameToken.OpCode = OPCODE_NONAME; - - return pToken; -} - -const FunctionInfo* FormulaParserImpl::getFuncInfoFromLibFuncName( const ApiToken& rToken ) const -{ - // try to parse calls to library functions - if( (rToken.OpCode == OPCODE_BAD) && rToken.Data.has< OUString >() ) - { - // format of the function call is "[n]!funcname", n being the link to the library - OUString aString = rToken.Data.get< OUString >(); - sal_Int32 nBracketOpen = aString.indexOf( '[' ); - sal_Int32 nBracketClose = aString.indexOf( ']' ); - sal_Int32 nExclamation = aString.indexOf( '!' ); - if( (0 == nBracketOpen) && (nBracketOpen + 1 < nBracketClose) && (nBracketClose + 1 == nExclamation) && (nExclamation + 1 < aString.getLength()) ) - { - sal_Int32 nRefId = aString.copy( nBracketOpen + 1, nBracketClose - nBracketOpen - 1 ).toInt32(); - const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get(); - if( pExtLink && (pExtLink->getLinkType() == LINKTYPE_LIBRARY) ) - if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aString.copy( nExclamation + 1 ).toAsciiUpperCase() ) ) - if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == pExtLink->getFuncLibraryType()) ) - return pFuncInfo; - } - } - return 0; -} - -bool FormulaParserImpl::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* FormulaParserImpl::getExternCallToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const -{ - const ApiToken* pECToken = 0; - while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; - if( pToken < pTokenEnd ) pECToken = pToken++; - while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken; - return (pToken == pTokenEnd) ? pECToken : 0; -} - -const FunctionInfo* FormulaParserImpl::convertExternCallParam( ApiToken& orFuncToken, const ApiToken& rECToken ) const -{ - if( const FunctionInfo* pFuncInfo = getFuncInfoFromApiToken( rECToken ) ) - { - orFuncToken.OpCode = pFuncInfo->mnApiOpCode; - // programmatic add-in function name - if( (pFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && (pFuncInfo->maExtProgName.getLength() > 0) ) - orFuncToken.Data <<= pFuncInfo->maExtProgName; - // name of unsupported function, convert to OPCODE_BAD to preserve the name - else if( (pFuncInfo->mnApiOpCode == OPCODE_BAD) && (pFuncInfo->maOoxFuncName.getLength() > 0) ) - orFuncToken.Data <<= pFuncInfo->maOoxFuncName; - return pFuncInfo; - } - - if( (rECToken.OpCode == OPCODE_MACRO) || (rECToken.OpCode == OPCODE_BAD) ) - { - // macro call or unknown function name, move data to function token - orFuncToken = rECToken; - } - else if( rECToken.OpCode == OPCODE_NAME ) - { - // defined name used as function call, convert to OPCODE_BAD to preserve the name - sal_Int32 nTokenIndex = 0; - if( rECToken.Data >>= nTokenIndex ) - { - if( const DefinedName* pDefName = getDefinedNames().getByTokenIndex( nTokenIndex ).get() ) - { - orFuncToken.OpCode = OPCODE_BAD; - orFuncToken.Data <<= pDefName->getCalcName(); - } + 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; } -const ApiToken* FormulaParserImpl::skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +OUString FormulaParserImpl::resolveDefinedName( sal_Int32 nTokenIndex ) 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* FormulaParserImpl::findParameters( ParameterPosVector& rParams, - const ApiToken* pToken, const ApiToken* pTokenEnd ) const -{ - // push position of OPCODE_OPEN - OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaParserImpl::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), "FormulaParserImpl::findParameters - OPCODE_CLOSE expected" ); - rParams.push_back( pToken ); - return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd; -} - -void FormulaParserImpl::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ) -{ - (void)nParam; // prevent 'unused' warning - switch( rFuncInfo.mnOobFuncId ) - { - case OOBIN_FUNC_FLOOR: - case OOBIN_FUNC_CEILING: - OSL_ENSURE( nParam == 2, "FormulaParserImpl::appendCalcOnlyParameter - unexpected parameter index" ); - maTokenStorage.append< double >( OPCODE_PUSH, 1.0 ); - maTokenStorage.append( OPCODE_SEP ); - break; - } -} - -void FormulaParserImpl::appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount ) -{ - switch( rFuncInfo.mnOobFuncId ) - { - case OOBIN_FUNC_WEEKNUM: - if( nParamCount == 1 ) - { - maTokenStorage.append< double >( OPCODE_PUSH, 1.0 ); - maTokenStorage.append( OPCODE_SEP ); - } - break; - } -} - -void FormulaParserImpl::appendFinalToken( const ApiToken& rToken ) -{ - if( (rToken.OpCode == OPCODE_MACRO) && !rToken.Data.hasValue() ) - { - maTokenStorage.append( OPCODE_ARRAY_OPEN ); - maTokenStorage.append( OPCODE_PUSH, BiffHelper::calcDoubleFromError( BIFF_ERR_NAME ) ); - maTokenStorage.append( OPCODE_ARRAY_CLOSE ); - } - else - maTokenStorage.push_back( rToken ); + if( const DefinedName* pDefName = getDefinedNames().getByTokenIndex( nTokenIndex ).get() ) + return pDefName->getCalcName(); + return OUString(); } // OOX parser implementation ================================================== @@ -1228,7 +1242,7 @@ void FormulaParserImpl::appendFinalToken( const ApiToken& rToken ) class OoxFormulaParserImpl : public FormulaParserImpl { public: - explicit OoxFormulaParserImpl( const OpCodeProvider& rOpCodeProv ); + explicit OoxFormulaParserImpl( const FormulaParser& rParent ); virtual void importOoxFormula( FormulaContext& rContext, @@ -1270,48 +1284,30 @@ private: bool pushOobFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ); private: - Reference< XFormulaParser > mxParser; - PropertySet maParserProps; + 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 OpCodeProvider& rOpCodeProv ) : - FormulaParserImpl( rOpCodeProv ), +OoxFormulaParserImpl::OoxFormulaParserImpl( const FormulaParser& rParent ) : + FormulaParserImpl( rParent ), + maApiParser( rParent.getDocumentFactory(), rParent ), mnAddDataPos( 0 ), mbNeedExtRefs( true ) { - try - { - mxParser.set( getDocumentFactory()->createInstance( CREATE_OUSTRING( "com.sun.star.sheet.FormulaParser" ) ), UNO_QUERY_THROW ); - } - catch( Exception& ) - { - } - OSL_ENSURE( mxParser.is(), "OoxFormulaParserImpl::OoxFormulaParserImpl - cannot create formula parser" ); - maParserProps.set( mxParser ); - maParserProps.setProperty( PROP_CompileEnglish, true ); - maParserProps.setProperty( PROP_FormulaConvention, ::com::sun::star::sheet::AddressConvention::XL_OOX ); - maParserProps.setProperty( PROP_IgnoreLeadingSpaces, false ); - maParserProps.setProperty( PROP_OpCodeMap, getOoxParserMap() ); } -void OoxFormulaParserImpl::importOoxFormula( - FormulaContext& rContext, const OUString& rFormulaString ) +void OoxFormulaParserImpl::importOoxFormula( FormulaContext& rContext, const OUString& rFormulaString ) { - if( mxParser.is() ) + if( mbNeedExtRefs ) { - if( mbNeedExtRefs ) - { - maParserProps.setProperty( PROP_ExternalLinks, getExternalLinks().getLinkInfos() ); - mbNeedExtRefs = false; - } - maParserProps.setProperty( PROP_ReferencePosition, rContext.getBaseAddress() ); - initializeImport( rContext ); - finalizeImport( mxParser->parseFormula( rFormulaString ) ); + maApiParser.getParserProperties().setProperty( PROP_ExternalLinks, getExternalLinks().getLinkInfos() ); + mbNeedExtRefs = false; } + initializeImport( rContext ); + finalizeImport( maApiParser.parseFormula( rFormulaString, rContext.getBaseAddress() ) ); } void OoxFormulaParserImpl::importOobFormula( FormulaContext& rContext, RecordInputStream& rStrm ) @@ -1848,7 +1844,7 @@ bool lclIsValidNlrRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow class BiffFormulaParserImpl : public FormulaParserImpl { public: - explicit BiffFormulaParserImpl( const OpCodeProvider& rOpCodeProv ); + explicit BiffFormulaParserImpl( const FormulaParser& rParent ); virtual void importBiffFormula( FormulaContext& rContext, @@ -1949,8 +1945,8 @@ private: // ---------------------------------------------------------------------------- -BiffFormulaParserImpl::BiffFormulaParserImpl( const OpCodeProvider& rOpCodeProv ) : - FormulaParserImpl( rOpCodeProv ), +BiffFormulaParserImpl::BiffFormulaParserImpl( const FormulaParser& rParent ) : + FormulaParserImpl( rParent ), mnAddDataPos( 0 ), mnCurrRefId( 0 ) { diff --git a/oox/source/xls/makefile.mk b/oox/source/xls/makefile.mk index 617b30389931..42a92edfe863 100644 --- a/oox/source/xls/makefile.mk +++ b/oox/source/xls/makefile.mk @@ -68,6 +68,7 @@ SLOFILES = \ $(SLO)$/formulabase.obj \ $(SLO)$/formulaparser.obj \ $(SLO)$/numberformatsbuffer.obj \ + $(SLO)$/ooxformulaparser.obj \ $(SLO)$/pagesettings.obj \ $(SLO)$/pivotcachebuffer.obj \ $(SLO)$/pivotcachefragment.obj \ diff --git a/oox/source/xls/ooxformulaparser.cxx b/oox/source/xls/ooxformulaparser.cxx new file mode 100644 index 000000000000..62e8189bdd00 --- /dev/null +++ b/oox/source/xls/ooxformulaparser.cxx @@ -0,0 +1,228 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ooxformulaparser.cxx,v $ + * $Revision: 1.1 $ + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "oox/xls/ooxformulaparser.hxx" +#include <com/sun/star/uno/XComponentContext.hpp> +#include "oox/xls/formulaparser.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::XComponentContext; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::sheet::FormulaToken; + +namespace oox { +namespace xls { + +// ============================================================================ + +class OOXMLFormulaParserImpl : private FormulaFinalizer +{ +public: + explicit OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxFactory ); + + Sequence< FormulaToken > parseFormula( const OUString& rFormula, const CellAddress& rReferencePos ); + +protected: + virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const; + +private: + ApiParserWrapper maApiParser; +}; + +// ---------------------------------------------------------------------------- + +OOXMLFormulaParserImpl::OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxFactory ) : + FormulaFinalizer( OpCodeProvider( rxFactory, FILTER_OOX, BIFF_UNKNOWN, true ) ), + maApiParser( rxFactory, *this ) +{ +} + +Sequence< FormulaToken > OOXMLFormulaParserImpl::parseFormula( const OUString& rFormula, const CellAddress& rReferencePos ) +{ + return finalizeTokenArray( maApiParser.parseFormula( rFormula, rReferencePos ) ); +} + +const FunctionInfo* OOXMLFormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const +{ + /* Try to parse calls to library functions. The format of such a function + call is assumed to be + "'<path-to-office-install>\Library\<libname>'!<funcname>". */ + + // the string has to start with an apostroph (followed by the library URL) + if( (rTokenData.getLength() >= 6) && (rTokenData[ 0 ] == '\'') ) + { + // library URL and function name are separated by an exclamation mark + sal_Int32 nExclamPos = rTokenData.lastIndexOf( '!' ); + if( (1 < nExclamPos) && (nExclamPos + 1 < rTokenData.getLength()) && (rTokenData[ nExclamPos - 1 ] == '\'') ) + { + // find the last backslash that separates library path and name + sal_Int32 nFileSep = rTokenData.lastIndexOf( '\\', nExclamPos - 2 ); + if( nFileSep > 1 ) + { + // find preceding backslash that separates the last directory name + sal_Int32 nDirSep = rTokenData.lastIndexOf( '\\', nFileSep - 1 ); + // function library is located in a directory called 'library' + if( (nDirSep > 0) && rTokenData.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "\\LIBRARY\\" ), nDirSep ) ) + { + // try to find a function info for the function name + OUString aFuncName = rTokenData.copy( nExclamPos + 1 ).toAsciiUpperCase(); + const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName ); + if( pFuncInfo && (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) ) + { + // check that the name of the library matches + OUString aLibName = rTokenData.copy( nFileSep + 1, nExclamPos - nFileSep - 2 ); + if( pFuncInfo->meFuncLibType == getFuncLibTypeFromLibraryName( aLibName ) ) + return pFuncInfo; + } + } + } + } + } + return 0; +} + +// ============================================================================ + +class OOXMLFormulaPrinterImpl : public OpCodeProvider +{ +public: + explicit OOXMLFormulaPrinterImpl( const Reference< XMultiServiceFactory >& rxFactory ); + +private: + ApiParserWrapper maApiParser; +}; + +// ---------------------------------------------------------------------------- + +OOXMLFormulaPrinterImpl::OOXMLFormulaPrinterImpl( const Reference< XMultiServiceFactory >& rxFactory ) : + OpCodeProvider( rxFactory, FILTER_OOX, BIFF_UNKNOWN, false ), + maApiParser( rxFactory, *this ) +{ +} + +// ============================================================================ + +Sequence< OUString > OOXMLFormulaParser_getSupportedServiceNames() +{ + Sequence< OUString > aServiceNames( 1 ); + aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.sheet.FilterFormulaParser" ); + return aServiceNames; +} + +OUString OOXMLFormulaParser_getImplementationName() +{ + return CREATE_OUSTRING( "com.sun.star.comp.oox.OOXMLFormulaParser" ); +} + +Reference< XInterface > SAL_CALL OOXMLFormulaParser_createInstance( const Reference< XComponentContext >& ) throw( Exception ) +{ + return static_cast< ::cppu::OWeakObject* >( new OOXMLFormulaParser ); +} + +// ============================================================================ + +OOXMLFormulaParser::OOXMLFormulaParser() +{ +} + +OOXMLFormulaParser::~OOXMLFormulaParser() +{ +} + +// com.sun.star.lang.XServiceInfo interface ----------------------------------- + +OUString SAL_CALL OOXMLFormulaParser::getImplementationName() throw( RuntimeException ) +{ + return OOXMLFormulaParser_getImplementationName(); +} + +sal_Bool SAL_CALL OOXMLFormulaParser::supportsService( const OUString& rService ) throw( RuntimeException ) +{ + const Sequence< OUString > aServices( OOXMLFormulaParser_getSupportedServiceNames() ); + const OUString* pArray = aServices.getConstArray(); + const OUString* pArrayEnd = pArray + aServices.getLength(); + return ::std::find( pArray, pArrayEnd, rService ) != pArrayEnd; +} + +Sequence< OUString > SAL_CALL OOXMLFormulaParser::getSupportedServiceNames() throw( RuntimeException ) +{ + return OOXMLFormulaParser_getSupportedServiceNames(); +} + +// com.sun.star.lang.XInitialization interface -------------------------------- + +void SAL_CALL OOXMLFormulaParser::initialize( const Sequence< Any >& rArgs ) throw( Exception, RuntimeException ) +{ + OSL_ENSURE( rArgs.hasElements(), "OOXMLFormulaParser::initialize - missing arguments" ); + if( !rArgs.hasElements() ) + throw RuntimeException(); + mxComponent.set( rArgs[ 0 ], UNO_QUERY_THROW ); +} + +// com.sun.star.sheet.XFilterFormulaParser interface -------------------------- + +OUString SAL_CALL OOXMLFormulaParser::getSupportedNamespace() throw( RuntimeException ) +{ + return CREATE_OUSTRING( "http://schemas.microsoft.com/office/excel/formula" ); +} + +// com.sun.star.sheet.XFormulaParser interface -------------------------------- + +Sequence< FormulaToken > SAL_CALL OOXMLFormulaParser::parseFormula( + const OUString& rFormula, const CellAddress& rReferencePos ) throw( RuntimeException ) +{ + if( !mxParserImpl ) + { + Reference< XMultiServiceFactory > xFactory( mxComponent, UNO_QUERY_THROW ); + mxParserImpl.reset( new OOXMLFormulaParserImpl( xFactory ) ); + } + return mxParserImpl->parseFormula( rFormula, rReferencePos ); +} + +OUString SAL_CALL OOXMLFormulaParser::printFormula( + const Sequence< FormulaToken >& /*rTokens*/, const CellAddress& /*rReferencePos*/ ) throw( RuntimeException ) +{ + // not implemented + throw RuntimeException(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + |