[Libreoffice-commits] core.git: 2 commits - sc/inc sc/qa sc/source
Kohei Yoshida
kohei.yoshida at collabora.com
Fri Feb 21 14:50:50 PST 2014
sc/inc/document.hxx | 2
sc/qa/unit/data/ods/shared-formula/3d-reference.ods |binary
sc/qa/unit/subsequent_export-test.cxx | 95 ++++++++++++++++++++
sc/source/filter/excel/xeformula.cxx | 27 +++--
sc/source/filter/excel/xetable.cxx | 53 ++++++++++-
sc/source/filter/inc/xeformula.hxx | 3
sc/source/filter/inc/xetable.hxx | 16 ++-
7 files changed, 183 insertions(+), 13 deletions(-)
New commits:
commit 180f593fbfea238df97d006f6847bba3d9b0e317
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Fri Feb 21 17:43:28 2014 -0500
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
diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx
index bdf7180..835a49d 100644
--- a/sc/source/filter/excel/xeformula.cxx
+++ b/sc/source/filter/excel/xeformula.cxx
@@ -322,6 +322,9 @@ public:
/** Returns true, if the passed formula type allows 3D references only. */
bool Is3DRefOnly( XclFormulaType eType ) const;
+ bool IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const;
+
// ------------------------------------------------------------------------
private:
const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
@@ -390,8 +393,6 @@ private:
// reference handling -----------------------------------------------------
SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
- bool IsRef2D( const ScSingleRefData& rRefData ) const;
- bool IsRef2D( const ScComplexRefData& rRefData ) const;
void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
@@ -1814,13 +1815,13 @@ SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
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
the own sheet. If 3D references are allowed, the passed reference does
not count as 2D reference. */
- if (mxData->mpLinkMgr && rRefData.IsFlag3D())
+ if (bCheck3DFlag && rRefData.IsFlag3D())
return false;
if (rRefData.IsTabDeleted())
@@ -1832,9 +1833,9 @@ bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData ) const
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(
@@ -1937,7 +1938,7 @@ void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos);
// 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)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
@@ -1982,7 +1983,7 @@ void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos);
// 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)
sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
@@ -2662,6 +2663,14 @@ XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
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: */
diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx
index be28b58..89a778b 100644
--- a/sc/source/filter/excel/xetable.cxx
+++ b/sc/source/filter/excel/xetable.cxx
@@ -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(
const ScFormulaCell& rScCell, const ScAddress& rScPos )
{
@@ -215,7 +254,19 @@ XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
// This formula cell is not shared formula cell.
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() )
{
// create a new record
diff --git a/sc/source/filter/inc/xeformula.hxx b/sc/source/filter/inc/xeformula.hxx
index 615c5a3..6dbee75 100644
--- a/sc/source/filter/inc/xeformula.hxx
+++ b/sc/source/filter/inc/xeformula.hxx
@@ -79,6 +79,9 @@ public:
@descr This is used i.e. for linked macros in push buttons. */
XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+ bool IsRef2D( const ScSingleRefData& rRefData ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData ) const;
+
private:
typedef boost::shared_ptr< XclExpFmlaCompImpl > XclExpFmlaCompImplRef;
XclExpFmlaCompImplRef mxImpl;
diff --git a/sc/source/filter/inc/xetable.hxx b/sc/source/filter/inc/xetable.hxx
index a04aae1..3299e60 100644
--- a/sc/source/filter/inc/xetable.hxx
+++ b/sc/source/filter/inc/xetable.hxx
@@ -31,6 +31,8 @@
#include "xestyle.hxx"
#include "xeextlst.hxx"
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <map>
@@ -192,8 +194,18 @@ public:
XclExpShrfmlaRef CreateOrExtendShrfmla( const ScFormulaCell& rScCell, const ScAddress& rScPos );
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 ========================================================
commit 23d9591c6e31a76555eb7c52d46359713a48c9e9
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date: Fri Feb 21 12:06:05 2014 -0500
fdo#74345: Minimalistic test to reproduce this.
Change-Id: Ibaa25f974ff98a8a601acbab25041b16d3b8dee2
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 12b2bee..0ab8400 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -658,7 +658,7 @@ public:
SC_DLLPUBLIC void SetActiveScenario( SCTAB nTab, bool bActive ); // only for Undo etc.
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const;
SC_DLLPUBLIC formula::FormulaGrammar::Grammar GetGrammar() const;
- void SetGrammar( formula::FormulaGrammar::Grammar eGram );
+ SC_DLLPUBLIC void SetGrammar( formula::FormulaGrammar::Grammar eGram );
SC_DLLPUBLIC sal_uInt8 GetLinkMode( SCTAB nTab ) const;
bool IsLinked( SCTAB nTab ) const;
SC_DLLPUBLIC const OUString GetLinkDoc( SCTAB nTab ) const;
diff --git a/sc/qa/unit/data/ods/shared-formula/3d-reference.ods b/sc/qa/unit/data/ods/shared-formula/3d-reference.ods
new file mode 100644
index 0000000..5148e82
Binary files /dev/null and b/sc/qa/unit/data/ods/shared-formula/3d-reference.ods differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index e834ca6..3a669ba 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -32,6 +32,7 @@
#include "scopetools.hxx"
#include "cellvalue.hxx"
#include <postit.hxx>
+#include <tokenstringcontext.hxx>
#include "svx/svdoole2.hxx"
#include "tabprotection.hxx"
@@ -43,6 +44,7 @@
#include "editeng/section.hxx"
#include <editeng/crossedoutitem.hxx>
#include <editeng/borderline.hxx>
+#include <formula/grammar.hxx>
#include <com/sun/star/table/BorderLineStyle.hpp>
@@ -85,6 +87,8 @@ public:
void testCellBordersXLS();
void testCellBordersXLSX();
+ void testSharedFormulaExportXLS();
+
CPPUNIT_TEST_SUITE(ScExportTest);
CPPUNIT_TEST(test);
#if !defined(MACOSX) && !defined(DRAGONFLY)
@@ -108,6 +112,7 @@ public:
CPPUNIT_TEST(testSheetProtectionXLSX);
CPPUNIT_TEST(testCellBordersXLS);
CPPUNIT_TEST(testCellBordersXLSX);
+ CPPUNIT_TEST(testSharedFormulaExportXLS);
CPPUNIT_TEST_SUITE_END();
@@ -1080,6 +1085,96 @@ void ScExportTest::testCellBordersXLSX()
testExcelCellBorders(XLSX);
}
+void ScExportTest::testSharedFormulaExportXLS()
+{
+ struct
+ {
+ bool checkContent( ScDocument* pDoc )
+ {
+ formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
+ pDoc->SetGrammar(eGram);
+ sc::TokenStringContext aCxt(pDoc, eGram);
+
+ // Check the title row.
+
+ OUString aActual = pDoc->GetString(0,1,0);
+ OUString aExpected = "Response";
+ if (aActual != aExpected)
+ {
+ cerr << "Wrong content in A2: expected='" << aExpected << "', actual='" << aActual << "'" << endl;
+ return false;
+ }
+
+ aActual = pDoc->GetString(1,1,0);
+ aExpected = "Response";
+ if (aActual != aExpected)
+ {
+ cerr << "Wrong content in B2: expected='" << aExpected << "', actual='" << aActual << "'" << endl;
+ return false;
+ }
+
+ // A3:A12 and B3:B12 are numbers from 1 to 10.
+ for (SCROW i = 0; i <= 9; ++i)
+ {
+ double fExpected = i + 1.0;
+ ScAddress aPos(0,i+2,0);
+ double fActual = pDoc->GetValue(aPos);
+ if (fExpected != fActual)
+ {
+ cerr << "Wrong value in A" << (i+2) << ": expected=" << fExpected << ", actual=" << fActual << endl;
+ return false;
+ }
+
+ aPos.IncCol();
+ ScFormulaCell* pFC = pDoc->GetFormulaCell(aPos);
+ if (!pFC)
+ {
+ cerr << "B" << (i+2) << " should be a formula cell." << endl;
+ return false;
+ }
+
+ OUString aFormula = pFC->GetCode()->CreateString(aCxt, aPos);
+ aExpected = "Coefficients!RC[-1]";
+ if (aFormula != aExpected)
+ {
+ cerr << "Wrong formula in B" << (i+2) << ": expected='" << aExpected << "', actual='" << aFormula << "'" << endl;
+ return false;
+ }
+
+ fActual = pDoc->GetValue(aPos);
+ if (fExpected != fActual)
+ {
+ cerr << "Wrong value in B" << (i+2) << ": expected=" << fExpected << ", actual=" << fActual << endl;
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ } aTest;
+
+ ScDocShellRef xDocSh = loadDoc("shared-formula/3d-reference.", ODS);
+ CPPUNIT_ASSERT_MESSAGE("Failed to load file.", xDocSh.Is());
+ ScDocument* pDoc = xDocSh->GetDocument();
+
+ // Check the content of the original.
+ bool bRes = aTest.checkContent(pDoc);
+ CPPUNIT_ASSERT_MESSAGE("Content check on the original document failed.", bRes);
+
+ ScDocShellRef xDocSh2 = saveAndReload(xDocSh, XLS);
+ xDocSh->DoClose();
+ CPPUNIT_ASSERT_MESSAGE("Failed to reload file.", xDocSh2.Is());
+
+ pDoc = xDocSh2->GetDocument();
+
+ // Check the content of the reloaded. This should be identical.
+ bRes = aTest.checkContent(pDoc);
+ CPPUNIT_ASSERT_MESSAGE("Content check on the reloaded document failed.", bRes);
+
+ xDocSh2->DoClose();
+}
+
ScExportTest::ScExportTest()
: ScBootstrapFixture("/sc/qa/unit/data")
{
More information about the Libreoffice-commits
mailing list