sc/inc/dpcache.hxx                    |    3 
 sc/inc/dpobject.hxx                   |   13 +--
 sc/inc/dpsave.hxx                     |    7 +
 sc/inc/dpshttab.hxx                   |    2 
 sc/inc/dptabdat.hxx                   |    2 
 sc/source/core/data/dpcache.cxx       |  137 +++++++++++++++++++++++++++++-----
 sc/source/core/data/dpobject.cxx      |   64 ++++++++-------
 sc/source/core/data/dpsave.cxx        |   57 ++++++++++++++
 sc/source/core/data/dpshttab.cxx      |    7 +
 sc/source/core/data/dptabdat.cxx      |    4 
 sc/source/ui/inc/gridwin.hxx          |    1 
 sc/source/ui/view/gridwin.cxx         |    4 
 sc/source/ui/view/gridwin_dbgutil.cxx |   14 +++
 13 files changed, 263 insertions(+), 52 deletions(-)

New commits:
commit 13d52f6af376cfe7f9bb44d52a831f43053002fe
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Tue Nov 11 23:00:21 2025 +0900
Commit:     Tomaž Vajngerl <[email protected]>
CommitDate: Fri Nov 14 10:09:06 2025 +0100

    sc: add dumpAsXml for the pivot table
    
    This adds dumpAsXml for the pivot table, which is triggered with
    ctrl+shift+alt F5 when the cursor is inside a pivot table. This
    will write the pivot_table_dump.xml into the CWD. The output
    includes the layout and the cache, which is similar to the pivot
    table dump, that has written the content to the console output.
    
    Change-Id: Ib20089cb63846aafe691a53bdb4e53f16602d5a8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193832
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>

diff --git a/sc/inc/dpcache.hxx b/sc/inc/dpcache.hxx
index f74258599beb..6d9462f989be 100644
--- a/sc/inc/dpcache.hxx
+++ b/sc/inc/dpcache.hxx
@@ -37,6 +37,7 @@ struct ScQueryParam;
 class ScDPObject;
 class ScDocument;
 struct ScInterpreterContext;
+namespace tools { class XmlWriter; }
 
 enum class SvNumFormatType : sal_Int16;
 
@@ -203,6 +204,8 @@ public:
     ScDPCache(ScDocument& rDoc);
     ~ScDPCache();
 
+    void dumpAsXml(tools::XmlWriter& rWriter) const;
+
 #if DUMP_PIVOT_TABLE
     void Dump() const;
 #endif
diff --git a/sc/inc/dpobject.hxx b/sc/inc/dpobject.hxx
index fb137dab306a..1bad3dc00fd3 100644
--- a/sc/inc/dpobject.hxx
+++ b/sc/inc/dpobject.hxx
@@ -55,7 +55,11 @@ namespace com::sun::star {
     }
 }
 
-namespace tools { class Rectangle; }
+namespace tools
+{
+    class Rectangle;
+    class XmlWriter;
+}
 class ScDPSaveData;
 class ScDPOutput;
 struct ScImportSourceDesc;
@@ -281,6 +285,9 @@ public:
         return { false, css::uno::Any() };
     }
 
+    void dumpAsXml(tools::XmlWriter& rWriter) const;
+    void dumpXmlFile() const;
+
 #if DUMP_PIVOT_TABLE
     void Dump() const;
     void DumpCache() const;
@@ -435,10 +442,6 @@ public:
     bool IntersectsTableByRows( SCCOL nCol, SCROW nRow1, SCROW nRow2, SCTAB 
nTab ) const;
     bool HasTable( const ScRange& rRange ) const;
 
-#if DEBUG_PIVOT_TABLE
-    void DumpTables() const;
-#endif
-
 private:
     /** Only to be called from ScDPCache::RemoveReference(). */
     void RemoveCache(const ScDPCache* pCache);
diff --git a/sc/inc/dpsave.hxx b/sc/inc/dpsave.hxx
index ad1e393dc879..3490bcaa98ee 100644
--- a/sc/inc/dpsave.hxx
+++ b/sc/inc/dpsave.hxx
@@ -46,6 +46,7 @@ class ScDPDimensionSaveData;
 class ScDPTableData;
 enum class ScGeneralFunction;
 namespace sc { class PivotTableFormats; }
+namespace tools { class XmlWriter; }
 
 // classes to save Data Pilot settings
 
@@ -86,6 +87,8 @@ public:
     void WriteToSource( const css::uno::Reference<css::uno::XInterface>& 
xMember,
                             sal_Int32 nPosition );
 
+    void dumpAsXml(tools::XmlWriter& rWriter) const;
+
 #if DUMP_PIVOT_TABLE
     void Dump(int nIndent = 0) const;
 #endif
@@ -226,6 +229,8 @@ public:
 
     void RemoveObsoleteMembers(const MemberSetType& rMembers);
 
+    void dumpAsXml(tools::XmlWriter& rWriter) const;
+
 #if DUMP_PIVOT_TABLE
     void Dump(int nIndent = 0) const;
 #endif
@@ -365,6 +370,8 @@ public:
      */
     SC_DLLPUBLIC bool HasInvisibleMember(std::u16string_view rDimName) const;
 
+    void dumpAsXml(tools::XmlWriter& rWriter) const;
+
 #if DUMP_PIVOT_TABLE
     void Dump() const;
 #endif
diff --git a/sc/inc/dpshttab.hxx b/sc/inc/dpshttab.hxx
index c9b45755fbc6..1b5347988bda 100644
--- a/sc/inc/dpshttab.hxx
+++ b/sc/inc/dpshttab.hxx
@@ -115,6 +115,8 @@ public:
     virtual const ScDPFilteredCache&   GetCacheTable() const override;
     virtual void ReloadCacheTable() override;
 
+    virtual void dumpAsXml(tools::XmlWriter& rWriter) const override;
+
 #if DUMP_PIVOT_TABLE
     virtual void Dump() const override;
 #endif
diff --git a/sc/inc/dptabdat.hxx b/sc/inc/dptabdat.hxx
index 74830406aa31..059eff3324e8 100644
--- a/sc/inc/dptabdat.hxx
+++ b/sc/inc/dptabdat.hxx
@@ -48,6 +48,7 @@ class ScDPDimension;
 class ScDPLevel;
 class ScDPInitState;
 class ScDocument;
+namespace tools { class XmlWriter; }
 
 /**
  * Base class that abstracts different data source types of a datapilot
@@ -132,6 +133,7 @@ public:
     virtual sal_Int32                GetSourceDim( sal_Int32 nDim );
     virtual sal_Int32                Compare( sal_Int32 nDim, sal_Int32 
nDataId1, sal_Int32 nDataId2);
 
+    virtual void dumpAsXml(tools::XmlWriter& rWriter) const;
 #if DUMP_PIVOT_TABLE
     virtual void Dump() const;
 #endif
diff --git a/sc/source/core/data/dpcache.cxx b/sc/source/core/data/dpcache.cxx
index a4d38a91d3ad..1a1afb4a856b 100644
--- a/sc/source/core/data/dpcache.cxx
+++ b/sc/source/core/data/dpcache.cxx
@@ -31,6 +31,7 @@
 #include <dpnumgroupinfo.hxx>
 #include <columniterator.hxx>
 #include <cellvalue.hxx>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
 
 #include <comphelper/parallelsort.hxx>
 #include <rtl/math.hxx>
@@ -40,13 +41,10 @@
 #include <unotools/collatorwrapper.hxx>
 #include <svl/numformat.hxx>
 #include <svl/zforlist.hxx>
+#include <tools/XmlWriter.hxx>
 #include <o3tl/safeint.hxx>
 #include <osl/diagnose.h>
 
-#if DUMP_PIVOT_TABLE
-#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
-#endif
-
 // TODO : Threaded pivot cache operation is disabled until we can figure out
 // ways to make the edit engine and number formatter codes thread-safe in a
 // proper fashion.
@@ -1407,21 +1405,9 @@ sal_Int32 ScDPCache::GetGroupType(tools::Long nDim) const
     return 0;
 }
 
-#if DUMP_PIVOT_TABLE
-
-namespace {
 
-void dumpItems(const ScDPCache& rCache, tools::Long nDim, const 
ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
+namespace
 {
-    for (size_t i = 0; i < rItems.size(); ++i)
-        std::cout << "      " << (i+nOffset) << ": " << 
rCache.GetFormattedString(nDim, rItems[i], false) << std::endl;
-}
-
-void dumpSourceData(const ScDPCache& rCache, tools::Long nDim, const 
ScDPCache::ScDPItemDataVec& rItems, const ScDPCache::IndexArrayType& rArray)
-{
-    for (const auto& rIndex : rArray)
-        std::cout << "      '" << rCache.GetFormattedString(nDim, 
rItems[rIndex], false) << "'" << std::endl;
-}
 
 const char* getGroupTypeName(sal_Int32 nType)
 {
@@ -1438,13 +1424,126 @@ const char* getGroupTypeName(sal_Int32 nType)
         case sheet::DataPilotFieldGroupBy::HOURS:    return pNames[5];
         case sheet::DataPilotFieldGroupBy::MINUTES:  return pNames[6];
         case sheet::DataPilotFieldGroupBy::SECONDS:  return pNames[7];
-        default:
-            ;
     }
 
     return pNames[0];
 }
 
+void dumpItemsAsXml(tools::XmlWriter& rWriter, ScDPCache const& rCache, 
tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
+{
+    for (size_t i = 0; i < rItems.size(); ++i)
+    {
+        rWriter.startElement("item");
+        rWriter.attribute("index", i + nOffset);
+        rWriter.attribute("value", rCache.GetFormattedString(nDim, rItems[i], 
false));
+        rWriter.endElement();
+    }
+}
+
+void dumpSourceDataAsXml(tools::XmlWriter& rWriter, ScDPCache const& rCache, 
tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, const 
ScDPCache::IndexArrayType& rArray)
+{
+    for (const auto& rIndex : rArray)
+    {
+        rWriter.startElement("source_data");
+        rWriter.attribute("value", rCache.GetFormattedString(nDim, 
rItems[rIndex], false));
+        rWriter.endElement();
+    }
+}
+
+} // end anonymous namespace
+
+void ScDPCache::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    rWriter.startElement("cache");
+
+    {
+        size_t index = 0;
+        for (const auto& rxField : maFields)
+        {
+            rWriter.startElement("source");
+            const Field& rField = *rxField;
+
+            rWriter.attribute("dimension", GetDimensionName(index));
+            rWriter.attribute("id", index);
+            rWriter.attribute("item_count", rField.maItems.size());
+
+            dumpItemsAsXml(rWriter, *this, index, rField.maItems, 0);
+
+            if (rField.mpGroup)
+            {
+                auto const& rGroup = rField.mpGroup;
+                rWriter.attribute("group_item_count", rGroup->maItems.size());
+                rWriter.attribute("group_type", 
getGroupTypeName(rGroup->mnGroupType));
+
+                dumpItemsAsXml(rWriter, *this, index, rField.mpGroup->maItems, 
rField.maItems.size());
+            }
+
+            dumpSourceDataAsXml(rWriter, *this, index, rField.maItems, 
rField.maData);
+
+            index++;
+            rWriter.endElement();
+        }
+    }
+
+    {
+        size_t index = maFields.size();
+        for (const auto& rxGroupField : maGroupFields)
+        {
+            rWriter.startElement("group");
+            const GroupItems& rGroupitems = *rxGroupField;
+
+            rWriter.attribute("id", index);
+            rWriter.attribute("item_count", rGroupitems.maItems.size());
+            rWriter.attribute("group_type", 
getGroupTypeName(rGroupitems.mnGroupType) );
+
+            dumpItemsAsXml(rWriter, *this, index, rGroupitems.maItems, 0);
+
+            index++;
+            rWriter.endElement();
+        }
+    }
+
+    {
+        struct { SCROW start; SCROW end; bool empty; } aRange;
+        mdds::flat_segment_tree<SCROW, bool>::const_iterator it = 
maEmptyRows.begin(), itEnd = maEmptyRows.end();
+        if (it != itEnd)
+        {
+            aRange.start = it->first;
+            aRange.empty = it->second;
+
+            for (++it; it != itEnd; ++it)
+            {
+                aRange.end = it->first-1;
+
+                rWriter.startElement("empty_row");
+                rWriter.attribute("start", aRange.start);
+                rWriter.attribute("end", aRange.end);
+                rWriter.attribute("empty", (aRange.empty ? "yes" : "no"));
+                rWriter.endElement();
+                aRange.start = it->first;
+                aRange.empty = it->second;
+            }
+        }
+    }
+
+    rWriter.endElement();
+}
+
+#if DUMP_PIVOT_TABLE
+
+namespace {
+
+void dumpItems(const ScDPCache& rCache, tools::Long nDim, const 
ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
+{
+    for (size_t i = 0; i < rItems.size(); ++i)
+        std::cout << "      " << (i+nOffset) << ": " << 
rCache.GetFormattedString(nDim, rItems[i], false) << std::endl;
+}
+
+void dumpSourceData(const ScDPCache& rCache, tools::Long nDim, const 
ScDPCache::ScDPItemDataVec& rItems, const ScDPCache::IndexArrayType& rArray)
+{
+    for (const auto& rIndex : rArray)
+        std::cout << "      '" << rCache.GetFormattedString(nDim, 
rItems[rIndex], false) << "'" << std::endl;
+}
 }
 
 void ScDPCache::Dump() const
diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx
index 3c1cfaa7250a..23f4603f07c2 100644
--- a/sc/source/core/data/dpobject.cxx
+++ b/sc/source/core/data/dpobject.cxx
@@ -76,10 +76,12 @@
 #include <utility>
 #include <vcl/svapp.hxx>
 #include <vcl/weld.hxx>
+#include <tools/XmlWriter.hxx>
 
 #include <vector>
 #include <memory>
 #include <algorithm>
+#include <filesystem>
 
 using namespace com::sun::star;
 using ::com::sun::star::uno::Sequence;
@@ -2900,6 +2902,40 @@ uno::Reference<sheet::XDimensionsSupplier> 
ScDPObject::CreateSource( const ScDPS
     return xRet;
 }
 
+void ScDPObject::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    rWriter.startElement("pivot");
+    rWriter.attribute("name", GetName());
+
+    if (mpSaveData)
+        mpSaveData->dumpAsXml(rWriter);
+
+    if (mpTableData)
+    {
+        mpTableData->dumpAsXml(rWriter);
+        const ScDPCache &rCache = mpTableData->GetCacheTable().getCache();
+        rCache.dumpAsXml(rWriter);
+    }
+
+    rWriter.endElement();
+}
+
+void ScDPObject::dumpXmlFile() const
+{
+    auto aFilePath = std::filesystem::current_path() / "pivot_table_dump.xml";
+    OUString aFilePathString(aFilePath.u16string());
+
+    SAL_WARN("sc", "Dumping Pivot Table to " << aFilePathString);
+
+    SvFileStream aStream(aFilePathString, StreamMode::STD_READWRITE | 
StreamMode::TRUNC);
+    tools::XmlWriter aWriter(&aStream);
+    aWriter.startDocument();
+    aWriter.startElement("pivotTable");
+    dumpAsXml(aWriter);
+    aWriter.endElement();
+    aWriter.endDocument();
+}
+
 #if DUMP_PIVOT_TABLE
 
 void ScDPObject::Dump() const
@@ -3851,34 +3887,6 @@ bool ScDPCollection::HasTable( const ScRange& rRange ) 
const
     return std::any_of(maTables.begin(), maTables.end(), 
FindIntersectingTable(rRange));
 }
 
-#if DEBUG_PIVOT_TABLE
-
-namespace {
-
-struct DumpTable
-{
-    void operator() (const std::unique_ptr<ScDPObject>& rObj) const
-    {
-        cout << "-- '" << rObj->GetName() << "'" << endl;
-        ScDPSaveData* pSaveData = rObj->GetSaveData();
-        if (!pSaveData)
-            return;
-
-        pSaveData->Dump();
-
-        cout << endl; // blank line
-    }
-};
-
-}
-
-void ScDPCollection::DumpTables() const
-{
-    std::for_each(maTables.begin(), maTables.end(), DumpTable());
-}
-
-#endif
-
 void ScDPCollection::RemoveCache(const ScDPCache* pCache)
 {
     if (maSheetCaches.remove(pCache))
diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx
index fce29e78358a..90ede1217eb8 100644
--- a/sc/source/core/data/dpsave.cxx
+++ b/sc/source/core/data/dpsave.cxx
@@ -26,6 +26,7 @@
 #include <generalfunction.hxx>
 #include <dptabdat.hxx>
 #include <pivot/PivotTableFormats.hxx>
+#include <tools/XmlWriter.hxx>
 
 #include <sal/types.h>
 #include <sal/log.hxx>
@@ -156,6 +157,22 @@ void ScDPSaveMember::WriteToSource( const 
uno::Reference<uno::XInterface>& xMemb
         ScUnoHelpFunctions::SetOptionalPropertyValue(xMembProp, 
SC_UNO_DP_POSITION, nPosition);
 }
 
+void ScDPSaveMember::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    rWriter.startElement("member");
+    rWriter.attribute("name", aName);
+    if (mpLayoutName)
+        rWriter.attribute("layout_name", *mpLayoutName);
+    else
+        rWriter.attribute("layout_name", "(none)"_ostr);
+
+    if (nVisibleMode == SC_DPSAVEMODE_DONTKNOW)
+        rWriter.attribute("visibility", "(unknown)"_ostr);
+    else
+        rWriter.attribute("visibility", (nVisibleMode ? "visible"_ostr : 
"hidden"_ostr));
+    rWriter.endElement();
+}
+
 #if DUMP_PIVOT_TABLE
 
 void ScDPSaveMember::Dump(int nIndent) const
@@ -644,6 +661,37 @@ void ScDPSaveDimension::RemoveObsoleteMembers(const 
MemberSetType& rMembers)
     maMemberList.swap(aNew);
 }
 
+void ScDPSaveDimension::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    static constexpr auto sOrientNames = std::to_array<std::string_view>({
+        "hidden", "column", "row", "page", "data"
+    });
+
+    rWriter.startElement("dimension");
+    rWriter.attribute("name", aName);
+    if (nOrientation <= DataPilotFieldOrientation_DATA)
+        rWriter.attribute("orientation", 
OString(sOrientNames[sal_Int32(nOrientation)]));
+    else
+        rWriter.attribute("orientation", "invalid"_ostr);
+
+    if (mpLayoutName)
+        rWriter.attribute("layout_name", *mpLayoutName);
+
+    if (mpSubtotalName)
+        rWriter.attribute("subtotal_name", *mpSubtotalName);
+    else
+        rWriter.attribute("subtotal_name", "(none)"_ostr);
+
+    rWriter.attribute("data_layout", (bIsDataLayout ? "yes"_ostr : "no"_ostr));
+    rWriter.attribute("duplicate", (bDupFlag ? "yes"_ostr : "no"_ostr));
+
+    for (ScDPSaveMember* pMember : maMemberList)
+    {
+        pMember->dumpAsXml(rWriter);
+    }
+    rWriter.endElement();
+}
+
 #if DUMP_PIVOT_TABLE
 
 void ScDPSaveDimension::Dump(int nIndent) const
@@ -1333,6 +1381,15 @@ bool 
ScDPSaveData::HasInvisibleMember(std::u16string_view rDimName) const
     return pDim->HasInvisibleMember();
 }
 
+void ScDPSaveData::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    for (auto const& itDim: m_DimList)
+    {
+        const ScDPSaveDimension& rSaveDimension = *itDim;
+        rSaveDimension.dumpAsXml(rWriter);
+    }
+}
+
 #if DUMP_PIVOT_TABLE
 
 void ScDPSaveData::Dump() const
diff --git a/sc/source/core/data/dpshttab.cxx b/sc/source/core/data/dpshttab.cxx
index f8cfa6de61e9..f95a05fc4edf 100644
--- a/sc/source/core/data/dpshttab.cxx
+++ b/sc/source/core/data/dpshttab.cxx
@@ -32,6 +32,7 @@
 #include <queryentry.hxx>
 
 #include <osl/diagnose.h>
+#include <tools/XmlWriter.hxx>
 
 #include <vector>
 
@@ -208,6 +209,12 @@ void ScSheetDPData::ReloadCacheTable()
     CreateCacheTable();
 }
 
+void ScSheetDPData::dumpAsXml(tools::XmlWriter& rWriter) const
+{
+    rWriter.startElement("sheet_data");
+    rWriter.endElement();
+}
+
 #if DUMP_PIVOT_TABLE
 
 void ScSheetDPData::Dump() const
diff --git a/sc/source/core/data/dptabdat.cxx b/sc/source/core/data/dptabdat.cxx
index d9706f961e2d..b0d2fab09871 100644
--- a/sc/source/core/data/dptabdat.cxx
+++ b/sc/source/core/data/dptabdat.cxx
@@ -282,6 +282,10 @@ sal_Int32 ScDPTableData::Compare( sal_Int32 nDim, 
sal_Int32 nDataId1, sal_Int32
         return -1;
 }
 
+void ScDPTableData::dumpAsXml(tools::XmlWriter& /*rWriter*/) const
+{
+}
+
 #if DUMP_PIVOT_TABLE
 void ScDPTableData::Dump() const
 {
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
index 228d5088f527..fa2ba12282b9 100644
--- a/sc/source/ui/inc/gridwin.hxx
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -531,6 +531,7 @@ private:
 
 #ifdef DBG_UTIL
     void dumpCellProperties();
+    void dumpPivotTable();
     void dumpColumnInformationPixel();
     void dumpColumnInformationHmm();
     void dumpGraphicInformation();
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 4e6e191c52a9..cbf6a586a949 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -3744,6 +3744,10 @@ void ScGridWindow::KeyInput(const KeyEvent& rKEvt)
             }
             return;
         }
+        else if (rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_F5)
+        {
+            dumpPivotTable();
+        }
     }
 
 #endif
diff --git a/sc/source/ui/view/gridwin_dbgutil.cxx 
b/sc/source/ui/view/gridwin_dbgutil.cxx
index aba730a9c8f4..f798bf3e017e 100644
--- a/sc/source/ui/view/gridwin_dbgutil.cxx
+++ b/sc/source/ui/view/gridwin_dbgutil.cxx
@@ -17,6 +17,8 @@
 #include <patattr.hxx>
 #include <userdat.hxx>
 #include <dpobject.hxx>
+#include <tools/XmlWriter.hxx>
+#include <filesystem>
 
 namespace {
 
@@ -63,6 +65,18 @@ void ScGridWindow::dumpColumnInformationHmm()
     }
 }
 
+void ScGridWindow::dumpPivotTable()
+{
+    ScAddress aPosition = mrViewData.GetCurPos();
+    ScDocument& rDocument = mrViewData.GetDocument();
+
+    const ScDPObject* pDPObject = rDocument.GetDPAtCursor(aPosition.Col(), 
aPosition.Row(), aPosition.Tab());
+    if (!pDPObject)
+        return;
+
+    pDPObject->dumpXmlFile();
+}
+
 void ScGridWindow::dumpCellProperties()
 {
     ScDocument& rDoc = mrViewData.GetDocument();

Reply via email to