From 7586151cbea2d3b93b42a42aa8dd157e75ca4f60 Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Tue, 15 Oct 2013 14:56:44 -0400 Subject: 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 --- formula/source/core/api/vectortoken.cxx | 6 +- include/formula/vectortoken.hxx | 26 ++++-- sc/source/core/data/formulacell.cxx | 4 +- sc/source/core/tool/formulagroup.cxx | 160 ++++++++++++++++++++++++-------- 4 files changed, 147 insertions(+), 49 deletions(-) diff --git a/formula/source/core/api/vectortoken.cxx b/formula/source/core/api/vectortoken.cxx index b752f5d669ba..206b9c9da8e8 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 9bc82f345e33..54043b114990 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. + * + *

If the whole data array consists of only numeric values, mpStringArray + * will be NULL, and NaN values in the numeric array represent empty + * cells.

+ * + *

If the whole data array consists of only string values, mpNumericArray + * will be NULL, and NULL values in the string array represent empty + * cells.

+ * + *

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.

+ */ 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 f3bf7fcbd059..dfcf352145d5 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 96ae83e46800..7f1bd747d78c 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 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(p); const formula::VectorRefArray& rArray = p2->GetArray(); - if (rArray.mbNumeric) - { - double fVal = fNan; - if (static_cast(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(i) < p2->GetArrayLength()) { - rtl_uString* pStr = NULL; - if (static_cast(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); } } -- cgit