[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