[Libreoffice-commits] core.git: Branch 'private/kohei/calc-shared-string' - 3 commits - formula/source include/formula sc/qa sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Tue Oct 15 13:06:59 PDT 2013


 formula/source/core/api/vectortoken.cxx |   11 +-
 include/formula/vectortoken.hxx         |   28 ++++-
 sc/qa/unit/ucalc.hxx                    |    2 
 sc/qa/unit/ucalc_formula.cxx            |   74 ++++++++++++++
 sc/source/core/data/formulacell.cxx     |    4 
 sc/source/core/tool/formulagroup.cxx    |  160 ++++++++++++++++++++++++--------
 6 files changed, 230 insertions(+), 49 deletions(-)

New commits:
commit cec617679b765611a17cd95a08f6e7029de989f7
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Oct 15 16:07:08 2013 -0400

    Right a new test case for fetching vector ref array.
    
    This currently fails rightly.
    
    Change-Id: Ic4d8d3d720b2ee879f963d1871dd8779461f352f

diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index 2c30f43..27bdaff 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -84,6 +84,7 @@ public:
     void testRangeList();
     void testInput();
 
+    void testFetchVectorRefArray();
     void testFormulaHashAndTag();
     void testFormulaRefData();
     void testFormulaCompiler();
@@ -288,6 +289,7 @@ public:
     CPPUNIT_TEST(testSharedStringPool);
     CPPUNIT_TEST(testRangeList);
     CPPUNIT_TEST(testInput);
+    CPPUNIT_TEST(testFetchVectorRefArray);
     CPPUNIT_TEST(testFormulaHashAndTag);
     CPPUNIT_TEST(testFormulaRefData);
     CPPUNIT_TEST(testFormulaCompiler);
diff --git a/sc/qa/unit/ucalc_formula.cxx b/sc/qa/unit/ucalc_formula.cxx
index e85c01c55..ba73d11 100644
--- a/sc/qa/unit/ucalc_formula.cxx
+++ b/sc/qa/unit/ucalc_formula.cxx
@@ -16,15 +16,89 @@
 #include "refdata.hxx"
 #include "scopetools.hxx"
 #include "formulacell.hxx"
+#include "formulagroup.hxx"
 #include "inputopt.hxx"
 #include "scmod.hxx"
 #include "docsh.hxx"
 #include "docfunc.hxx"
 
+#include "formula/vectortoken.hxx"
+
 #include <boost/scoped_ptr.hpp>
 
 using namespace formula;
 
+void Test::testFetchVectorRefArray()
+{
+    m_pDoc->InsertTab(0, "Test");
+
+    // All numeric cells in Column A.
+    m_pDoc->SetValue(ScAddress(0,0,0), 1);
+    m_pDoc->SetValue(ScAddress(0,1,0), 2);
+    m_pDoc->SetValue(ScAddress(0,2,0), 3);
+    m_pDoc->SetValue(ScAddress(0,3,0), 4);
+
+    sc::FormulaGroupContext aCxt;
+    formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(0,0,0), 4);
+    CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
+    CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
+    CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
+    CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
+    CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
+    CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
+
+    aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(0,0,0), 5);
+    CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
+    CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
+    CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
+    CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
+    CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
+    CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
+    CPPUNIT_ASSERT_MESSAGE("Empty cell should be represented by a NaN.", rtl::math::isNan(aArray.mpNumericArray[4]));
+
+    // All string cells in Column B.  Note that the fetched string arrays are
+    // only to be compared case-insensitively.  Right now, we use upper cased
+    // strings to achieve case-insensitive-ness, but that may change. So,
+    // don't count on that.
+    m_pDoc->SetString(ScAddress(1,0,0), "Andy");
+    m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
+    m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
+    m_pDoc->SetString(ScAddress(1,3,0), "David");
+    aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(1,0,0), 5);
+    CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
+    CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
+    CPPUNIT_ASSERT_MESSAGE("Failed on case in-sensitive equality test.", OUString(aArray.mpStringArray[0]).equalsIgnoreAsciiCaseAscii("Andy"));
+    CPPUNIT_ASSERT_MESSAGE("Failed on case in-sensitive equality test.", OUString(aArray.mpStringArray[1]).equalsIgnoreAsciiCaseAscii("Bruce"));
+    CPPUNIT_ASSERT_MESSAGE("Failed on case in-sensitive equality test.", OUString(aArray.mpStringArray[2]).equalsIgnoreAsciiCaseAscii("Charlie"));
+    CPPUNIT_ASSERT_MESSAGE("Failed on case in-sensitive equality test.", OUString(aArray.mpStringArray[3]).equalsIgnoreAsciiCaseAscii("David"));
+    CPPUNIT_ASSERT_MESSAGE("Empty cell should be represented by a NULL pointer.", !aArray.mpStringArray[4]);
+
+    // Mixture of numeric, string, and empty cells in Column C.
+    m_pDoc->SetString(ScAddress(2,0,0), "Header");
+    m_pDoc->SetValue(ScAddress(2,1,0), 11);
+    m_pDoc->SetValue(ScAddress(2,2,0), 12);
+    m_pDoc->SetValue(ScAddress(2,3,0), 13);
+    m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
+    m_pDoc->CalcAll();
+
+    aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(2,0,0), 7);
+    CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
+    CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
+    CPPUNIT_ASSERT_MESSAGE("Failed on case in-sensitive equality test.", OUString(aArray.mpStringArray[0]).equalsIgnoreAsciiCaseAscii("Header"));
+    CPPUNIT_ASSERT_MESSAGE("String value should be NULL for numeric cell.", aArray.mpStringArray[1] == NULL);
+    CPPUNIT_ASSERT_MESSAGE("String value should be NULL for numeric cell.", aArray.mpStringArray[2] == NULL);
+    CPPUNIT_ASSERT_MESSAGE("String value should be NULL for numeric cell.", aArray.mpStringArray[3] == NULL);
+    CPPUNIT_ASSERT_EQUAL(11.0, aArray.mpNumericArray[1]);
+    CPPUNIT_ASSERT_EQUAL(12.0, aArray.mpNumericArray[2]);
+    CPPUNIT_ASSERT_EQUAL(13.0, aArray.mpNumericArray[3]);
+    CPPUNIT_ASSERT_MESSAGE("This cell should be empty.", aArray.mpStringArray[4] == NULL && rtl::math::isNan(aArray.mpNumericArray[4]));
+    CPPUNIT_ASSERT_MESSAGE("String value should be NULL for numeric cell.", aArray.mpStringArray[5] == NULL);
+    CPPUNIT_ASSERT_EQUAL(36.0, aArray.mpNumericArray[5]);
+    CPPUNIT_ASSERT_MESSAGE("This cell should be empty.", aArray.mpStringArray[6] == NULL && rtl::math::isNan(aArray.mpNumericArray[6]));
+
+    m_pDoc->DeleteTab(0);
+}
+
 void Test::testFormulaHashAndTag()
 {
     m_pDoc->InsertTab(0, "Test");
commit f4a58f54f8a226839504b8c3461b0916cc80caa2
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Oct 15 16:04:22 2013 -0400

    More eye-pleasing way of checking for valid vector array...
    
    Change-Id: If2f47a7d98a4cbc9e09dc98c1bb0e11f8f889265

diff --git a/formula/source/core/api/vectortoken.cxx b/formula/source/core/api/vectortoken.cxx
index 206b9c9..55ab18e 100644
--- a/formula/source/core/api/vectortoken.cxx
+++ b/formula/source/core/api/vectortoken.cxx
@@ -15,6 +15,11 @@ VectorRefArray::VectorRefArray() : mpNumericArray(NULL), mpStringArray(NULL) {}
 VectorRefArray::VectorRefArray( const double* pArray ) : mpNumericArray(pArray), mpStringArray(NULL) {}
 VectorRefArray::VectorRefArray( rtl_uString** pArray ) : mpNumericArray(NULL), mpStringArray(pArray) {}
 
+bool VectorRefArray::isValid() const
+{
+    return mpNumericArray || mpStringArray;
+}
+
 SingleVectorRefToken::SingleVectorRefToken( const double* pArray, size_t nLength ) :
     FormulaToken(svSingleVectorRef, ocPush), maArray(pArray), mnArrayLength(nLength) {}
 
diff --git a/include/formula/vectortoken.hxx b/include/formula/vectortoken.hxx
index 54043b1..3b1db68 100644
--- a/include/formula/vectortoken.hxx
+++ b/include/formula/vectortoken.hxx
@@ -40,6 +40,8 @@ struct FORMULA_DLLPUBLIC VectorRefArray
     VectorRefArray();
     VectorRefArray( const double* pArray );
     VectorRefArray( rtl_uString** pArray );
+
+    bool isValid() const;
 };
 
 /**
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index dfcf352..9d32104 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3420,7 +3420,7 @@ public:
                         // length.
 
                         formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(mrCxt, aRefPos, nLen);
-                        if (!aArray.mpNumericArray && !aArray.mpStringArray)
+                        if (!aArray.isValid())
                             return false;
 
                         formula::SingleVectorRefToken aTok(aArray, nLen);
@@ -3488,7 +3488,7 @@ public:
                     {
                         aRefPos.SetCol(i);
                         formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(mrCxt, aRefPos, nArrayLength);
-                        if (!aArray.mpNumericArray && !aArray.mpStringArray)
+                        if (!aArray.isValid())
                             return false;
 
                         aArrays.push_back(aArray);
commit e647b2ff91fcfa5dda0278007148b05ee7fff2de
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Oct 15 14:56:44 2013 -0400

    Allow vector array tokens to store both numeric and string values.
    
    This is achieved by storing two physical arrays in each vector
    reference array.
    
    Change-Id: Iafb9e57b86e57e75eed8ff692a6d882c2049f710

diff --git a/formula/source/core/api/vectortoken.cxx b/formula/source/core/api/vectortoken.cxx
index b752f5d..206b9c9 100644
--- a/formula/source/core/api/vectortoken.cxx
+++ b/formula/source/core/api/vectortoken.cxx
@@ -11,9 +11,9 @@
 
 namespace formula {
 
-VectorRefArray::VectorRefArray() : mpNumericArray(NULL), mbNumeric(true) {}
-VectorRefArray::VectorRefArray( const double* pArray ) : mpNumericArray(pArray), mbNumeric(true) {}
-VectorRefArray::VectorRefArray( rtl_uString** pArray ) : mpStringArray(pArray), mbNumeric(false) {}
+VectorRefArray::VectorRefArray() : mpNumericArray(NULL), mpStringArray(NULL) {}
+VectorRefArray::VectorRefArray( const double* pArray ) : mpNumericArray(pArray), mpStringArray(NULL) {}
+VectorRefArray::VectorRefArray( rtl_uString** pArray ) : mpNumericArray(NULL), mpStringArray(pArray) {}
 
 SingleVectorRefToken::SingleVectorRefToken( const double* pArray, size_t nLength ) :
     FormulaToken(svSingleVectorRef, ocPush), maArray(pArray), mnArrayLength(nLength) {}
diff --git a/include/formula/vectortoken.hxx b/include/formula/vectortoken.hxx
index 9bc82f3..54043b1 100644
--- a/include/formula/vectortoken.hxx
+++ b/include/formula/vectortoken.hxx
@@ -14,14 +14,28 @@
 
 namespace formula {
 
+/**
+ * Single unit of vector reference consists of two physical arrays.
+ *
+ * <p>If the whole data array consists of only numeric values, mpStringArray
+ * will be NULL, and NaN values in the numeric array represent empty
+ * cells.</p>
+ *
+ * <p>If the whole data array consists of only string values, mpNumericArray
+ * will be NULL, and NULL values in the string array represent empty
+ * cells.</p>
+ *
+ * <p>If the data array consists of numeric and string values, then both
+ * mpNumericArray and mpStringArray will be non-NULL, and a string cell will
+ * be represented by a non-NULL pointer value in the string array.  If the
+ * string value is NULL, check the corresponding value in the numeric array.
+ * If the value in the numeric array is NaN, it's an empty cell, otherwise
+ * it's a numeric cell.</p>
+ */
 struct FORMULA_DLLPUBLIC VectorRefArray
 {
-    union {
-        const double* mpNumericArray;
-        rtl_uString** mpStringArray;
-    };
-
-    bool mbNumeric;
+    const double* mpNumericArray;
+    rtl_uString** mpStringArray;
 
     VectorRefArray();
     VectorRefArray( const double* pArray );
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index f3bf7fc..dfcf352 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3420,7 +3420,7 @@ public:
                         // length.
 
                         formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(mrCxt, aRefPos, nLen);
-                        if (!aArray.mpNumericArray)
+                        if (!aArray.mpNumericArray && !aArray.mpStringArray)
                             return false;
 
                         formula::SingleVectorRefToken aTok(aArray, nLen);
@@ -3488,7 +3488,7 @@ public:
                     {
                         aRefPos.SetCol(i);
                         formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(mrCxt, aRefPos, nArrayLength);
-                        if (!aArray.mpNumericArray)
+                        if (!aArray.mpNumericArray && !aArray.mpStringArray)
                             return false;
 
                         aArrays.push_back(aArray);
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index 96ae83e..7f1bd74 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -47,43 +47,43 @@ namespace {
  */
 void fillMatrix( ScMatrix& rMat, size_t nCol, const double* pNums, size_t nLen )
 {
-    const double* p = pNums;
-    const double* pEnd = p + nLen;
-    const double* pHead = NULL;
-    for (; p != pEnd; ++p)
+    const double* pNum = pNums;
+    const double* pNumEnd = pNum + nLen;
+    const double* pNumHead = NULL;
+    for (; pNum != pNumEnd; ++pNum)
     {
-        if (!rtl::math::isNan(*p))
+        if (!rtl::math::isNan(*pNum))
         {
-            if (!pHead)
+            if (!pNumHead)
                 // Store the first non-NaN position.
-                pHead = p;
+                pNumHead = pNum;
 
             continue;
         }
 
-        if (pHead)
+        if (pNumHead)
         {
             // Flush this non-NaN segment to the matrix.
-            rMat.PutDouble(pHead, p - pHead, nCol, pHead - pNums);
-            pHead = NULL;
+            rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
+            pNumHead = NULL;
         }
     }
 
-    if (pHead)
+    if (pNumHead)
     {
         // Flush last non-NaN segment to the matrix.
-        rMat.PutDouble(pHead, p - pHead, nCol, pHead - pNums);
+        rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
     }
 }
 
-void flushSegment(
+void flushStrSegment(
     ScMatrix& rMat, size_t nCol, rtl_uString** pHead, rtl_uString** pCur, rtl_uString** pTop )
 {
     size_t nOffset = pHead - pTop;
     std::vector<svl::SharedString> aStrs;
     aStrs.reserve(pCur - pHead);
     for (; pHead != pCur; ++pHead)
-        aStrs.push_back(svl::SharedString(*pHead, NULL));
+        aStrs.push_back(svl::SharedString(*pHead, *pHead));
 
     rMat.PutString(&aStrs[0], aStrs.size(), nCol, nOffset);
 }
@@ -107,7 +107,7 @@ void fillMatrix( ScMatrix& rMat, size_t nCol, rtl_uString** pStrs, size_t nLen )
         if (pHead)
         {
             // Flush this non-empty segment to the matrix.
-            flushSegment(rMat, nCol, pHead, p, pStrs);
+            flushStrSegment(rMat, nCol, pHead, p, pStrs);
             pHead = NULL;
         }
     }
@@ -115,7 +115,75 @@ void fillMatrix( ScMatrix& rMat, size_t nCol, rtl_uString** pStrs, size_t nLen )
     if (pHead)
     {
         // Flush last non-empty segment to the matrix.
-        flushSegment(rMat, nCol, pHead, p, pStrs);
+        flushStrSegment(rMat, nCol, pHead, p, pStrs);
+    }
+}
+
+void fillMatrix( ScMatrix& rMat, size_t nCol, const double* pNums, rtl_uString** pStrs, size_t nLen )
+{
+    if (!pStrs)
+    {
+        fillMatrix(rMat, nCol, pNums, nLen);
+        return;
+    }
+
+    const double* pNum = pNums;
+    const double* pNumHead = NULL;
+    rtl_uString** pStr = pStrs;
+    rtl_uString** pStrEnd = pStr + nLen;
+    rtl_uString** pStrHead = NULL;
+
+    for (; pStr != pStrEnd; ++pStr, ++pNum)
+    {
+        if (*pStr)
+        {
+            // String cell exists.
+
+            if (pNumHead)
+            {
+                // Flush this numeric segment to the matrix.
+                rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
+                pNumHead = NULL;
+            }
+
+            if (!pStrHead)
+                // Store the first non-empty string position.
+                pStrHead = pStr;
+
+            continue;
+        }
+
+        // No string cell. Check the numeric cell value.
+
+        if (pStrHead)
+        {
+            // Flush this non-empty string segment to the matrix.
+            flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
+            pStrHead = NULL;
+        }
+
+        if (!rtl::math::isNan(*pNum))
+        {
+            // Numeric cell exists.
+            if (!pNumHead)
+                // Store the first non-NaN position.
+                pNumHead = pNum;
+
+            continue;
+        }
+
+        // Empty cell. No action required.
+    }
+
+    if (pStrHead)
+    {
+        // Flush the last non-empty segment to the matrix.
+        flushStrSegment(rMat, nCol, pStrHead, pStr, pStrs);
+    }
+    else if (pNumHead)
+    {
+        // Flush the last numeric segment to the matrix.
+        rMat.PutDouble(pNumHead, pNum - pNumHead, nCol, pNumHead - pNums);
     }
 }
 
@@ -168,26 +236,28 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
                 {
                     const formula::SingleVectorRefToken* p2 = static_cast<const formula::SingleVectorRefToken*>(p);
                     const formula::VectorRefArray& rArray = p2->GetArray();
-                    if (rArray.mbNumeric)
-                    {
-                        double fVal = fNan;
-                        if (static_cast<size_t>(i) < p2->GetArrayLength())
-                            fVal = rArray.mpNumericArray[i];
 
-                        if (rtl::math::isNan(fVal))
-                            aCode2.AddToken(ScEmptyCellToken(false, false));
-                        else
-                            aCode2.AddDouble(fVal);
-                    }
-                    else
+                    rtl_uString* pStr = NULL;
+                    double fVal = fNan;
+                    if (static_cast<size_t>(i) < p2->GetArrayLength())
                     {
-                        rtl_uString* pStr = NULL;
-                        if (static_cast<size_t>(i) < p2->GetArrayLength())
+                        if (rArray.mpStringArray)
+                            // See if the cell is of string type.
                             pStr = rArray.mpStringArray[i];
 
-                        if (pStr)
-                            aCode2.AddString(OUString(pStr));
+                        if (!pStr && rArray.mpNumericArray)
+                            fVal = rArray.mpNumericArray[i];
                     }
+
+                    if (pStr)
+                        // This is a string cell.
+                        aCode2.AddString(OUString(pStr));
+                    else if (rtl::math::isNan(fVal))
+                        // Value of NaN represents an empty cell.
+                        aCode2.AddToken(ScEmptyCellToken(false, false));
+                    else
+                        // Numeric cell.
+                        aCode2.AddDouble(fVal);
                 }
                 break;
                 case formula::svDoubleVectorRef:
@@ -213,17 +283,31 @@ bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddres
                     for (size_t nCol = 0; nCol < nColSize; ++nCol)
                     {
                         const formula::VectorRefArray& rArray = rArrays[nCol];
-                        if (rArray.mbNumeric)
+                        if (rArray.mpStringArray)
                         {
-                            const double* pNums = rArray.mpNumericArray;
-                            pNums += nRowStart;
-                            fillMatrix(*pMat, nCol, pNums, nRowSize);
+                            if (rArray.mpNumericArray)
+                            {
+                                // Mixture of string and numeric values.
+                                const double* pNums = rArray.mpNumericArray;
+                                pNums += nRowStart;
+                                rtl_uString** pStrs = rArray.mpStringArray;
+                                pStrs += nRowStart;
+                                fillMatrix(*pMat, nCol, pNums, pStrs, nRowSize);
+                            }
+                            else
+                            {
+                                // String cells only.
+                                rtl_uString** pStrs = rArray.mpStringArray;
+                                pStrs += nRowStart;
+                                fillMatrix(*pMat, nCol, pStrs, nRowSize);
+                            }
                         }
                         else
                         {
-                            rtl_uString** pStrs = rArray.mpStringArray;
-                            pStrs += nRowStart;
-                            fillMatrix(*pMat, nCol, pStrs, nRowSize);
+                            // Numeric cells only.
+                            const double* pNums = rArray.mpNumericArray;
+                            pNums += nRowStart;
+                            fillMatrix(*pMat, nCol, pNums, nRowSize);
                         }
                     }
 


More information about the Libreoffice-commits mailing list