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.yosh...@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.yosh...@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.yosh...@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); } } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits