Kaydet (Commit) 180f593f authored tarafından Kohei Yoshida's avatar Kohei Yoshida

fdo#74345: Some shared formulas cannot be exported as shared formulas.

Excel's shared formula has some restrictions that Calc's doesn't have.
So we need to check each shared formula token to see if we can export
it as shared when saving to Excel.  Refer to the "SharedParsedFormula"
section of the [MS-XLS] spec.

Change-Id: I0ffce26700d2773bbd2893743edc6c03682c2ed7
üst 23d9591c
...@@ -322,6 +322,9 @@ public: ...@@ -322,6 +322,9 @@ public:
/** Returns true, if the passed formula type allows 3D references only. */ /** Returns true, if the passed formula type allows 3D references only. */
bool Is3DRefOnly( XclFormulaType eType ) const; bool Is3DRefOnly( XclFormulaType eType ) const;
bool IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const;
bool IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
private: private:
const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const; const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
...@@ -390,8 +393,6 @@ private: ...@@ -390,8 +393,6 @@ private:
// reference handling ----------------------------------------------------- // reference handling -----------------------------------------------------
SCTAB GetScTab( const ScSingleRefData& rRefData ) const; SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
bool IsRef2D( const ScSingleRefData& rRefData ) const;
bool IsRef2D( const ScComplexRefData& rRefData ) const;
void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos, void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const; bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
...@@ -1814,13 +1815,13 @@ SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const ...@@ -1814,13 +1815,13 @@ SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
return rRefData.toAbs(*mxData->mpScBasePos).Tab(); return rRefData.toAbs(*mxData->mpScBasePos).Tab();
} }
bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const
{ {
/* rRefData.IsFlag3D() determines if sheet name is always visible, even on /* rRefData.IsFlag3D() determines if sheet name is always visible, even on
the own sheet. If 3D references are allowed, the passed reference does the own sheet. If 3D references are allowed, the passed reference does
not count as 2D reference. */ not count as 2D reference. */
if (mxData->mpLinkMgr && rRefData.IsFlag3D()) if (bCheck3DFlag && rRefData.IsFlag3D())
return false; return false;
if (rRefData.IsTabDeleted()) if (rRefData.IsTabDeleted())
...@@ -1832,9 +1833,9 @@ bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const ...@@ -1832,9 +1833,9 @@ bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const
return rRefData.Tab() == GetCurrScTab(); return rRefData.Tab() == GetCurrScTab();
} }
bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData ) const bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const
{ {
return IsRef2D( rRefData.Ref1 ) && IsRef2D( rRefData.Ref2 ); return IsRef2D(rRefData.Ref1, bCheck3DFlag) && IsRef2D(rRefData.Ref2, bCheck3DFlag);
} }
void XclExpFmlaCompImpl::ConvertRefData( void XclExpFmlaCompImpl::ConvertRefData(
...@@ -1937,7 +1938,7 @@ void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData ) ...@@ -1937,7 +1938,7 @@ void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos); mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos);
// create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token
if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr))
{ {
// 2D reference (not in defined names, but allowed in range lists) // 2D reference (not in defined names, but allowed in range lists)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN : sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
...@@ -1982,7 +1983,7 @@ void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData ) ...@@ -1982,7 +1983,7 @@ void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos); mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos);
// create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token
if( !mxData->mrCfg.mb3DRefOnly && IsRef2D( aRefData ) ) if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr))
{ {
// 2D reference (not in name formulas, but allowed in range lists) // 2D reference (not in name formulas, but allowed in range lists)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN : sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
...@@ -2662,6 +2663,14 @@ XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula( ...@@ -2662,6 +2663,14 @@ XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
return mxImpl->CreateNameXFormula( nExtSheet, nExtName ); return mxImpl->CreateNameXFormula( nExtSheet, nExtName );
} }
// ============================================================================ bool XclExpFormulaCompiler::IsRef2D( const ScSingleRefData& rRefData ) const
{
return mxImpl->IsRef2D(rRefData, true);
}
bool XclExpFormulaCompiler::IsRef2D( const ScComplexRefData& rRefData ) const
{
return mxImpl->IsRef2D(rRefData, true);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -206,6 +206,45 @@ XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) : ...@@ -206,6 +206,45 @@ XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
{ {
} }
bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
{
using namespace formula;
FormulaToken** pTokens = rArray.GetArray();
sal_uInt16 nLen = rArray.GetLen();
for (sal_uInt16 i = 0; i < nLen; ++i)
{
const FormulaToken* p = pTokens[i];
switch (p->GetType())
{
case svSingleRef:
{
const ScSingleRefData& rRefData = static_cast<const ScToken*>(p)->GetSingleRef();
if (!GetFormulaCompiler().IsRef2D(rRefData))
// Excel's shared formula cannot include 3D reference.
return false;
}
break;
case svDoubleRef:
{
const ScComplexRefData& rRefData = static_cast<const ScToken*>(p)->GetDoubleRef();
if (!GetFormulaCompiler().IsRef2D(rRefData))
// Excel's shared formula cannot include 3D reference.
return false;
}
break;
case svExternalSingleRef:
case svExternalDoubleRef:
case svExternalName:
// External references aren't allowed.
return false;
default:
;
}
}
return true;
}
XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla( XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
const ScFormulaCell& rScCell, const ScAddress& rScPos ) const ScFormulaCell& rScCell, const ScAddress& rScPos )
{ {
...@@ -215,7 +254,19 @@ XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla( ...@@ -215,7 +254,19 @@ XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
// This formula cell is not shared formula cell. // This formula cell is not shared formula cell.
return xRec; return xRec;
XclExpShrfmlaMap::iterator aIt = maRecMap.find( pShrdScTokArr ); // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
if (maBadTokens.count(pShrdScTokArr) > 0)
// Already on the black list. Skip it.
return xRec;
if (!IsValidTokenArray(*pShrdScTokArr))
{
// We can't export this as shared formula.
maBadTokens.insert(pShrdScTokArr);
return xRec;
}
TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
if( aIt == maRecMap.end() ) if( aIt == maRecMap.end() )
{ {
// create a new record // create a new record
......
...@@ -79,6 +79,9 @@ public: ...@@ -79,6 +79,9 @@ public:
@descr This is used i.e. for linked macros in push buttons. */ @descr This is used i.e. for linked macros in push buttons. */
XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName ); XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
bool IsRef2D( const ScSingleRefData& rRefData ) const;
bool IsRef2D( const ScComplexRefData& rRefData ) const;
private: private:
typedef boost::shared_ptr< XclExpFmlaCompImpl > XclExpFmlaCompImplRef; typedef boost::shared_ptr< XclExpFmlaCompImpl > XclExpFmlaCompImplRef;
XclExpFmlaCompImplRef mxImpl; XclExpFmlaCompImplRef mxImpl;
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include "xestyle.hxx" #include "xestyle.hxx"
#include "xeextlst.hxx" #include "xeextlst.hxx"
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <map> #include <map>
...@@ -192,8 +194,18 @@ public: ...@@ -192,8 +194,18 @@ public:
XclExpShrfmlaRef CreateOrExtendShrfmla( const ScFormulaCell& rScCell, const ScAddress& rScPos ); XclExpShrfmlaRef CreateOrExtendShrfmla( const ScFormulaCell& rScCell, const ScAddress& rScPos );
private: private:
typedef ::std::map< const ScTokenArray*, XclExpShrfmlaRef > XclExpShrfmlaMap; /**
XclExpShrfmlaMap maRecMap; /// Map containing the SHRFMLA records. * Check for presence of token that's not allowed in Excel's shared
* formula. Refer to the "SharedParsedFormula" section of [MS-XLS] spec
* for more info.
*/
bool IsValidTokenArray( const ScTokenArray& rArray ) const;
typedef boost::unordered_map<const ScTokenArray*, XclExpShrfmlaRef> TokensType;
typedef boost::unordered_set<const ScTokenArray*> BadTokenArraysType;
TokensType maRecMap; /// Map containing the SHRFMLA records.
BadTokenArraysType maBadTokens; /// shared tokens we should *not* export as SHRFMLA
}; };
// Multiple operations ======================================================== // Multiple operations ========================================================
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment