From 1d85c8df2fb9cb25fc524485339ae9f11e8da676 Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Fri, 6 Dec 2013 10:43:38 -0500 Subject: fdo#69518: Correctly handle the old constraint syntax. We apparently support this syntax for ODF-backward compatibility. To fix this, I resurrected ScDPObject::ParseFilters() which was removed as unused, adjusted it for the String->OUString change, and changed the filter data structure to the UNO one rather than using the old one we no longer use elsewhere. Change-Id: If52b38aaa1e8b208fb0ef9d92a6e853decdf43e3 --- sc/inc/dpobject.hxx | 6 + sc/inc/dpoutput.hxx | 21 -- sc/source/core/data/dpobject.cxx | 441 +++++++++++++++++++++++++++++++++++++++ sc/source/core/tool/interpr2.cxx | 23 +- 4 files changed, 466 insertions(+), 25 deletions(-) diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx index b2f6e380d8c9..1683480d7d2b 100644 --- a/sc/inc/dpobject.hxx +++ b/sc/inc/dpobject.hxx @@ -183,6 +183,12 @@ public: const OUString& rDataFieldName, std::vector& rFilters); + bool ParseFilters( + OUString& rDataFieldName, + std::vector& rFilters, + std::vector& rFilterFuncs, + const OUString& rFilterList ); + void GetMemberResultNames(ScDPUniqueStringSet& rNames, long nDimension); void ToggleDetails(const ::com::sun::star::sheet::DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj); diff --git a/sc/inc/dpoutput.hxx b/sc/inc/dpoutput.hxx index 293d93767356..7b38c7eee227 100644 --- a/sc/inc/dpoutput.hxx +++ b/sc/inc/dpoutput.hxx @@ -41,29 +41,8 @@ namespace com { namespace sun { namespace star { namespace sheet { class Rectangle; class ScDocument; - struct ScDPOutLevelData; - -struct ScDPGetPivotDataField -{ - OUString maFieldName; - com::sun::star::sheet::GeneralFunction meFunction; - - bool mbValIsStr; - OUString maValStr; - double mnValNum; - - ScDPGetPivotDataField() : - meFunction( com::sun::star::sheet::GeneralFunction_NONE ), - mbValIsStr( false ), - mnValNum( 0.0 ) - { - } -}; - - - class ScDPOutput { private: diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx index 4dca25862ab8..83532e26e054 100644 --- a/sc/source/core/data/dpobject.cxx +++ b/sc/source/core/data/dpobject.cxx @@ -1400,6 +1400,447 @@ void ScDPObject::GetMemberResultNames(ScDPUniqueStringSet& rNames, long nDimensi pOutput->GetMemberResultNames(rNames, nDimension); // used only with table data -> level not needed } +namespace { + +bool dequote( const OUString& rSource, sal_Int32 nStartPos, sal_Int32& rEndPos, OUString& rResult ) +{ + // nStartPos has to point to opening quote + + bool bRet = false; + const sal_Unicode cQuote = '\''; + + if (rSource[nStartPos] == cQuote) + { + OUStringBuffer aBuffer; + sal_Int32 nPos = nStartPos + 1; + const sal_Int32 nLen = rSource.getLength(); + + while ( nPos < nLen ) + { + const sal_Unicode cNext = rSource[nPos]; + if ( cNext == cQuote ) + { + if (nPos+1 < nLen && rSource[nPos+1] == cQuote) + { + // double quote is used for an embedded quote + aBuffer.append( cNext ); // append one quote + ++nPos; // skip the next one + } + else + { + // end of quoted string + rResult = aBuffer.makeStringAndClear(); + rEndPos = nPos + 1; // behind closing quote + return true; + } + } + else + aBuffer.append( cNext ); + + ++nPos; + } + // no closing quote before the end of the string -> error (bRet still false) + } + + return bRet; +} + +struct ScGetPivotDataFunctionEntry +{ + const sal_Char* pName; + sheet::GeneralFunction eFunc; +}; + +bool parseFunction( const OUString& rList, sal_Int32 nStartPos, sal_Int32& rEndPos, sheet::GeneralFunction& rFunc ) +{ + static const ScGetPivotDataFunctionEntry aFunctions[] = + { + // our names + { "Sum", sheet::GeneralFunction_SUM }, + { "Count", sheet::GeneralFunction_COUNT }, + { "Average", sheet::GeneralFunction_AVERAGE }, + { "Max", sheet::GeneralFunction_MAX }, + { "Min", sheet::GeneralFunction_MIN }, + { "Product", sheet::GeneralFunction_PRODUCT }, + { "CountNums", sheet::GeneralFunction_COUNTNUMS }, + { "StDev", sheet::GeneralFunction_STDEV }, + { "StDevp", sheet::GeneralFunction_STDEVP }, + { "Var", sheet::GeneralFunction_VAR }, + { "VarP", sheet::GeneralFunction_VARP }, + // compatibility names + { "Count Nums", sheet::GeneralFunction_COUNTNUMS }, + { "StdDev", sheet::GeneralFunction_STDEV }, + { "StdDevp", sheet::GeneralFunction_STDEVP } + }; + + const sal_Int32 nListLen = rList.getLength(); + while (nStartPos < nListLen && rList[nStartPos] == ' ') + ++nStartPos; + + bool bParsed = false; + bool bFound = false; + OUString aFuncStr; + sal_Int32 nFuncEnd = 0; + if (nStartPos < nListLen && rList[nStartPos] == '\'') + bParsed = dequote( rList, nStartPos, nFuncEnd, aFuncStr ); + else + { + nFuncEnd = rList.indexOf(']', nStartPos); + if (nFuncEnd >= 0) + { + aFuncStr = rList.copy(nStartPos, nFuncEnd - nStartPos); + bParsed = true; + } + } + + if ( bParsed ) + { + aFuncStr = comphelper::string::strip(aFuncStr, ' '); + + const sal_Int32 nFuncCount = sizeof(aFunctions) / sizeof(aFunctions[0]); + for ( sal_Int32 nFunc=0; nFunc= 0) + { + sal_Int32 nNameEnd = nClosePos; + sal_Int32 nSemiPos = rList.indexOf(';', nStartPos); + if (nSemiPos >= 0 && nSemiPos < nClosePos && pFunc) + { + sal_Int32 nFuncEnd = 0; + if (parseFunction(rList, nSemiPos+1, nFuncEnd, *pFunc)) + nNameEnd = nSemiPos; + } + + aDequoted = rList.copy(nStartPos, nNameEnd - nStartPos); + // spaces before the closing bracket or semicolon + aDequoted = comphelper::string::stripEnd(aDequoted, ' '); + nQuoteEnd = nClosePos + 1; + bParsed = true; + } + } + } + + if ( bParsed && ScGlobal::GetpTransliteration()->isEqual( aDequoted, rSearch ) ) + { + nMatchList = nQuoteEnd; // match count in the list string, including quotes + nMatchSearch = rSearch.getLength(); + } + } + else + { + // otherwise look for search string at the start of rList + ScGlobal::GetpTransliteration()->equals( + rList, 0, rList.getLength(), nMatchList, rSearch, 0, rSearch.getLength(), nMatchSearch); + } + + if (nMatchSearch == rSearch.getLength()) + { + // search string is at start of rList - look for following space or end of string + + bool bValid = false; + if ( sal::static_int_cast(nMatchList) >= rList.getLength() ) + bValid = true; + else + { + sal_Unicode cNext = rList[nMatchList]; + if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) ) + bValid = true; + } + + if ( bValid ) + { + rMatched = nMatchList; + return true; + } + } + + return false; +} + +} // anonymous namespace + +bool ScDPObject::ParseFilters( + OUString& rDataFieldName, + std::vector& rFilters, + std::vector& rFilterFuncs, const OUString& rFilterList ) +{ + // parse the string rFilterList into parameters for GetPivotData + + CreateObjects(); // create xSource if not already done + + std::vector aDataNames; // data fields (source name) + std::vector aGivenNames; // data fields (compound name) + std::vector aFieldNames; // column/row/data fields + std::vector< uno::Sequence > aFieldValues; + + // + // get all the field and item names + // + + uno::Reference xDimsName = xSource->getDimensions(); + uno::Reference xIntDims = new ScNameToIndexAccess( xDimsName ); + sal_Int32 nDimCount = xIntDims->getCount(); + for ( sal_Int32 nDim = 0; nDim xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nDim) ); + uno::Reference xDim( xIntDim, uno::UNO_QUERY ); + uno::Reference xDimProp( xDim, uno::UNO_QUERY ); + uno::Reference xDimSupp( xDim, uno::UNO_QUERY ); + sal_Bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + OUString(SC_UNO_DP_ISDATALAYOUT) ); + sal_Int32 nOrient = ScUnoHelpFunctions::GetEnumProperty( + xDimProp, OUString(SC_UNO_DP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( !bDataLayout ) + { + if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) + { + OUString aSourceName; + OUString aGivenName; + ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim ); + aDataNames.push_back( aSourceName ); + aGivenNames.push_back( aGivenName ); + } + else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN ) + { + // get level names, as in ScDPOutput + + uno::Reference xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() ); + sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, + OUString(SC_UNO_DP_USEDHIERARCHY) ); + if ( nHierarchy >= xHiers->getCount() ) + nHierarchy = 0; + + uno::Reference xHier = ScUnoHelpFunctions::AnyToInterface( + xHiers->getByIndex(nHierarchy) ); + uno::Reference xHierSupp( xHier, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() ); + sal_Int32 nLevCount = xLevels->getCount(); + for (sal_Int32 nLev=0; nLev xLevel = ScUnoHelpFunctions::AnyToInterface( + xLevels->getByIndex(nLev) ); + uno::Reference xLevNam( xLevel, uno::UNO_QUERY ); + uno::Reference xLevSupp( xLevel, uno::UNO_QUERY ); + if ( xLevNam.is() && xLevSupp.is() ) + { + uno::Reference xMembers = xLevSupp->getMembers(); + + OUString aFieldName( xLevNam->getName() ); + uno::Sequence aMemberNames( xMembers->getElementNames() ); + + aFieldNames.push_back( aFieldName ); + aFieldValues.push_back( aMemberNames ); + } + } + } + } + } + } + + // + // compare and build filters + // + + SCSIZE nDataFields = aDataNames.size(); + SCSIZE nFieldCount = aFieldNames.size(); + OSL_ENSURE( aGivenNames.size() == nDataFields && aFieldValues.size() == nFieldCount, "wrong count" ); + + bool bError = false; + bool bHasData = false; + OUString aRemaining(comphelper::string::strip(rFilterList, ' ')); + while (!aRemaining.isEmpty() && !bError) + { + bool bUsed = false; + + // look for data field name + + for ( SCSIZE nDataPos=0; nDataPos& rItems = aFieldValues[nField]; + sal_Int32 nItemCount = rItems.getLength(); + const OUString* pItemArr = rItems.getConstArray(); + for ( sal_Int32 nItem=0; nItem aFilters; - svl::SharedString aDataFieldName; + OUString aDataFieldName; ScRange aBlock; if (bOldSyntax) { - aDataFieldName = GetString(); + aDataFieldName = GetString().getString(); switch (GetStackType()) { @@ -2887,7 +2887,7 @@ void ScInterpreter::ScGetPivotData() return; } - aDataFieldName = GetString(); // First parameter is data field name. + aDataFieldName = GetString().getString(); // First parameter is data field name. } // NOTE : MS Excel docs claim to use the 'most recent' which is not @@ -2900,7 +2900,22 @@ void ScInterpreter::ScGetPivotData() return; } - double fVal = pDPObj->GetPivotData(aDataFieldName.getString(), aFilters); + if (bOldSyntax) + { + OUString aFilterStr = aDataFieldName; + std::vector aFilterFuncs; + if (!pDPObj->ParseFilters(aDataFieldName, aFilters, aFilterFuncs, aFilterStr)) + { + PushError(errNoRef); + return; + } + + // TODO : For now, we ignore filter functions since we couldn't find a + // live example of how they are supposed to be used. We'll support + // this again once we come across a real-world example. + } + + double fVal = pDPObj->GetPivotData(aDataFieldName, aFilters); if (rtl::math::isNan(fVal)) { PushError(errNoRef); -- cgit v1.2.3