[Libreoffice-commits] core.git: sc/inc sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Fri Dec 6 08:53:16 PST 2013


 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(-)

New commits:
commit 1d85c8df2fb9cb25fc524485339ae9f11e8da676
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Dec 6 10:43:38 2013 -0500

    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

diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx
index b2f6e38..1683480 100644
--- a/sc/inc/dpobject.hxx
+++ b/sc/inc/dpobject.hxx
@@ -183,6 +183,12 @@ public:
         const OUString& rDataFieldName,
         std::vector<com::sun::star::sheet::DataPilotFieldFilter>& rFilters);
 
+    bool ParseFilters(
+        OUString& rDataFieldName,
+        std::vector<com::sun::star::sheet::DataPilotFieldFilter>& rFilters,
+        std::vector<com::sun::star::sheet::GeneralFunction>& 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 293d937..7b38c7e 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 4dca258..83532e2 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<nFuncCount && !bFound; nFunc++ )
+        {
+            if (aFuncStr.equalsIgnoreAsciiCaseAscii(aFunctions[nFunc].pName))
+            {
+                rFunc = aFunctions[nFunc].eFunc;
+                bFound = true;
+
+                while (nFuncEnd < nListLen && rList[nFuncEnd] == ' ')
+                    ++nFuncEnd;
+                rEndPos = nFuncEnd;
+            }
+        }
+    }
+
+    return bFound;
+}
+
+bool isAtStart(
+    const OUString& rList, const OUString& rSearch, sal_Int32& rMatched,
+    bool bAllowBracket, sheet::GeneralFunction* pFunc )
+{
+    sal_Int32 nMatchList = 0;
+    sal_Int32 nMatchSearch = 0;
+    sal_Unicode cFirst = rList[0];
+    if ( cFirst == '\'' || cFirst == '[' )
+    {
+        // quoted string or string in brackets must match completely
+
+        OUString aDequoted;
+        sal_Int32 nQuoteEnd = 0;
+        bool bParsed = false;
+
+        if ( cFirst == '\'' )
+            bParsed = dequote( rList, 0, nQuoteEnd, aDequoted );
+        else if ( cFirst == '[' )
+        {
+            // skip spaces after the opening bracket
+
+            sal_Int32 nStartPos = 1;
+            const sal_Int32 nListLen = rList.getLength();
+            while (nStartPos < nListLen && rList[nStartPos] == ' ')
+                ++nStartPos;
+
+            if (rList[nStartPos] == '\'')         // quoted within the brackets?
+            {
+                if ( dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
+                {
+                    // after the quoted string, there must be the closing bracket, optionally preceded by spaces,
+                    // and/or a function name
+                    while (nQuoteEnd < nListLen && rList[nQuoteEnd] == ' ')
+                        ++nQuoteEnd;
+
+                    // semicolon separates function name
+                    if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ';' && pFunc)
+                    {
+                        sal_Int32 nFuncEnd = 0;
+                        if ( parseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
+                            nQuoteEnd = nFuncEnd;
+                    }
+                    if (nQuoteEnd < nListLen && rList[nQuoteEnd] == ']')
+                    {
+                        ++nQuoteEnd;        // include the closing bracket for the matched length
+                        bParsed = true;
+                    }
+                }
+            }
+            else
+            {
+                // implicit quoting to the closing bracket
+
+                sal_Int32 nClosePos = rList.indexOf(']', nStartPos);
+                if (nClosePos >= 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<sal_Int32>(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<sheet::DataPilotFieldFilter>& rFilters,
+    std::vector<sheet::GeneralFunction>& rFilterFuncs, const OUString& rFilterList )
+{
+    // parse the string rFilterList into parameters for GetPivotData
+
+    CreateObjects();            // create xSource if not already done
+
+    std::vector<OUString> aDataNames;     // data fields (source name)
+    std::vector<OUString> aGivenNames;    // data fields (compound name)
+    std::vector<OUString> aFieldNames;    // column/row/data fields
+    std::vector< uno::Sequence<OUString> > aFieldValues;
+
+    //
+    // get all the field and item names
+    //
+
+    uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+    uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
+    sal_Int32 nDimCount = xIntDims->getCount();
+    for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ )
+    {
+        uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nDim) );
+        uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY );
+        uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
+        uno::Reference<sheet::XHierarchiesSupplier> 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<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
+                sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
+                                                    OUString(SC_UNO_DP_USEDHIERARCHY) );
+                if ( nHierarchy >= xHiers->getCount() )
+                    nHierarchy = 0;
+
+                uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface(
+                                                    xHiers->getByIndex(nHierarchy) );
+                uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
+                if ( xHierSupp.is() )
+                {
+                    uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
+                    sal_Int32 nLevCount = xLevels->getCount();
+                    for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
+                    {
+                        uno::Reference<uno::XInterface> xLevel = ScUnoHelpFunctions::AnyToInterface(
+                                                            xLevels->getByIndex(nLev) );
+                        uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
+                        uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY );
+                        if ( xLevNam.is() && xLevSupp.is() )
+                        {
+                            uno::Reference<container::XNameAccess> xMembers = xLevSupp->getMembers();
+
+                            OUString aFieldName( xLevNam->getName() );
+                            uno::Sequence<OUString> 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<nDataFields && !bUsed; nDataPos++ )
+        {
+            OUString aFound;
+            sal_Int32 nMatched = 0;
+            if (isAtStart(aRemaining, aDataNames[nDataPos], nMatched, false, NULL))
+                aFound = aDataNames[nDataPos];
+            else if (isAtStart(aRemaining, aGivenNames[nDataPos], nMatched, false, NULL))
+                aFound = aGivenNames[nDataPos];
+
+            if (!aFound.isEmpty())
+            {
+                rDataFieldName = aFound;
+                aRemaining = aRemaining.copy(nMatched);
+                bHasData = true;
+                bUsed = true;
+            }
+        }
+
+        // look for field name
+
+        OUString aSpecField;
+        bool bHasFieldName = false;
+        if ( !bUsed )
+        {
+            sal_Int32 nMatched = 0;
+            for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ )
+            {
+                if (isAtStart(aRemaining, aFieldNames[nField], nMatched, true, NULL))
+                {
+                    aSpecField = aFieldNames[nField];
+                    aRemaining = aRemaining.copy(nMatched);
+                    aRemaining = comphelper::string::stripStart(aRemaining, ' ');
+
+                    // field name has to be followed by item name in brackets
+                    if (aRemaining[0] == '[')
+                    {
+                        bHasFieldName = true;
+                        // bUsed remains false - still need the item
+                    }
+                    else
+                    {
+                        bUsed = true;
+                        bError = true;
+                    }
+                }
+            }
+        }
+
+        // look for field item
+
+        if ( !bUsed )
+        {
+            bool bItemFound = false;
+            sal_Int32 nMatched = 0;
+            OUString aFoundName;
+            OUString aFoundValue;
+            sheet::GeneralFunction eFunc = sheet::GeneralFunction_NONE;
+            sheet::GeneralFunction eFoundFunc = sheet::GeneralFunction_NONE;
+
+            for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
+            {
+                // If a field name is given, look in that field only, otherwise in all fields.
+                // aSpecField is initialized from aFieldNames array, so exact comparison can be used.
+                if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
+                {
+                    const uno::Sequence<OUString>& rItems = aFieldValues[nField];
+                    sal_Int32 nItemCount = rItems.getLength();
+                    const OUString* pItemArr = rItems.getConstArray();
+                    for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
+                    {
+                        if ( isAtStart( aRemaining, pItemArr[nItem], nMatched, false, &eFunc ) )
+                        {
+                            if ( bItemFound )
+                                bError = true;      // duplicate (also across fields)
+                            else
+                            {
+                                aFoundName = aFieldNames[nField];
+                                aFoundValue = pItemArr[nItem];
+                                eFoundFunc = eFunc;
+                                bItemFound = true;
+                                bUsed = true;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if ( bItemFound && !bError )
+            {
+                sheet::DataPilotFieldFilter aField;
+                aField.FieldName = aFoundName;
+                aField.MatchValue = aFoundValue;
+                rFilters.push_back(aField);
+                rFilterFuncs.push_back(eFoundFunc);
+                aRemaining = aRemaining.copy(nMatched);
+            }
+        }
+
+        if ( !bUsed )
+            bError = true;
+
+        // remove any number of spaces between entries
+        aRemaining = comphelper::string::stripStart(aRemaining, ' ');
+    }
+
+    if ( !bError && !bHasData && aDataNames.size() == 1 )
+    {
+        // if there's only one data field, its name need not be specified
+        rDataFieldName = aDataNames[0];
+        bHasData = true;
+    }
+
+    return bHasData && !bError;
+}
+
 void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj)
 {
     CreateObjects();            // create xSource if not already done
diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx
index 27b0af9..ab9183a 100644
--- a/sc/source/core/tool/interpr2.cxx
+++ b/sc/source/core/tool/interpr2.cxx
@@ -2831,12 +2831,12 @@ void ScInterpreter::ScGetPivotData()
     }
 
     std::vector<sheet::DataPilotFieldFilter> 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<sheet::GeneralFunction> 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);


More information about the Libreoffice-commits mailing list