LibreOffice Module sc (master) 1
xepivotxml.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <xepivotxml.hxx>
11#include <dpcache.hxx>
12#include <dpdimsave.hxx>
13#include <dpitemdata.hxx>
14#include <dpobject.hxx>
15#include <dpsave.hxx>
16#include <dputil.hxx>
17#include <document.hxx>
18#include <generalfunction.hxx>
19#include <unonames.hxx>
20#include <xestyle.hxx>
21#include <xeroot.hxx>
22
23#include <o3tl/temporary.hxx>
24#include <o3tl/safeint.hxx>
25#include <oox/export/utils.hxx>
26#include <oox/token/namespaces.hxx>
27#include <sal/log.hxx>
29#include <sax/fastattribs.hxx>
30#include <svl/numformat.hxx>
31
32#include <com/sun/star/beans/XPropertySet.hpp>
33#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
34#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
35#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
36#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
37#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
38
39#include <vector>
40
41using namespace oox;
42using namespace com::sun::star;
43
44namespace {
45
46void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache )
47{
48 SCROW nCount = rCache.GetDataSize();
49 size_t nFieldCount = rCache.GetFieldCount();
50
51 sax_fastparser::FSHelperPtr& pRecStrm = rStrm.GetCurrentStream();
52 pRecStrm->startElement(XML_pivotCacheRecords,
53 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
54 FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
55 XML_count, OString::number(static_cast<tools::Long>(nCount)));
56
57 for (SCROW i = 0; i < nCount; ++i)
58 {
59 pRecStrm->startElement(XML_r);
60 for (size_t nField = 0; nField < nFieldCount; ++nField)
61 {
62 const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField);
63 assert(pArray);
64 assert(o3tl::make_unsigned(i) < pArray->size());
65
66 // We are using XML_x reference (like: <x v="0"/>), instead of values here (eg: <s v="No Discount"/>).
67 // That's why in SavePivotCacheXml method, we need to list all items.
68 pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]));
69 }
70 pRecStrm->endElement(XML_r);
71 }
72
73 pRecStrm->endElement(XML_pivotCacheRecords);
74}
75
76const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient )
77{
78 switch (eOrient)
79 {
80 case sheet::DataPilotFieldOrientation_COLUMN:
81 return "axisCol";
82 case sheet::DataPilotFieldOrientation_ROW:
83 return "axisRow";
84 case sheet::DataPilotFieldOrientation_PAGE:
85 return "axisPage";
86 case sheet::DataPilotFieldOrientation_DATA:
87 return "axisValues";
88 case sheet::DataPilotFieldOrientation_HIDDEN:
89 default:
90 ;
91 }
92
93 return "";
94}
95
96const char* toOOXMLSubtotalType(ScGeneralFunction eFunc)
97{
98 switch (eFunc)
99 {
101 return "sum";
103 return "count";
105 return "average";
107 return "max";
109 return "min";
111 return "product";
113 return "countNums";
115 return "stdDev";
117 return "stdDevp";
119 return "var";
121 return "varp";
122 default:
123 ;
124 }
125 return nullptr;
126}
127
128}
129
131 XclExpRoot(rRoot) {}
132
134{
135 sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream();
136 pWorkbookStrm->startElement(XML_pivotCaches);
137
138 for (size_t i = 0, n = maCaches.size(); i < n; ++i)
139 {
140 const Entry& rEntry = maCaches[i];
141
142 sal_Int32 nCacheId = i + 1;
143 OUString aRelId;
144 sax_fastparser::FSHelperPtr pPCStrm = rStrm.CreateOutputStream(
145 XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId),
146 XclXmlUtils::GetStreamName(nullptr, "pivotCache/pivotCacheDefinition", nCacheId),
147 rStrm.GetCurrentStream()->getOutputStream(),
148 CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"),
149 CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
150 &aRelId);
151
152 pWorkbookStrm->singleElement(XML_pivotCache,
153 XML_cacheId, OString::number(nCacheId),
154 FSNS(XML_r, XML_id), aRelId.toUtf8());
155
156 rStrm.PushStream(pPCStrm);
157 SavePivotCacheXml(rStrm, rEntry, nCacheId);
158 rStrm.PopStream();
159 }
160
161 pWorkbookStrm->endElement(XML_pivotCaches);
162}
163
164void XclExpXmlPivotCaches::SetCaches( std::vector<Entry>&& rCaches )
165{
166 maCaches = std::move(rCaches);
167}
168
170{
171 return !maCaches.empty();
172}
173
175{
176 if (nCacheId <= 0)
177 // cache ID is 1-based.
178 return nullptr;
179
180 size_t nPos = nCacheId - 1;
181 if (nPos >= maCaches.size())
182 return nullptr;
183
184 return &maCaches[nPos];
185}
186
187namespace {
196OUString GetExcelFormattedDate( double fSerialDateTime, const SvNumberFormatter& rFormatter )
197{
198 // tdf#125055: properly round the value to seconds when truncating nanoseconds below
199 constexpr double fHalfSecond = 1 / 86400.0 * 0.5;
200 css::util::DateTime aUDateTime
201 = (DateTime(rFormatter.GetNullDate()) + fSerialDateTime + fHalfSecond).GetUNODateTime();
202 // We need to reset nanoseconds, to avoid string like: "1982-02-18T16:04:47.999999849"
203 aUDateTime.NanoSeconds = 0;
204 OUStringBuffer sBuf;
205 ::sax::Converter::convertDateTime(sBuf, aUDateTime, nullptr, true);
206 return sBuf.makeStringAndClear();
207}
208
209// Excel seems to expect different order of group item values; we need to rearrange elements
210// to output "<date1" first, then elements, then ">date2" last.
211// Since ScDPItemData::DateFirst is -1, ScDPItemData::DateLast is 10000, and other date group
212// items would fit between those in order (like 0 = Jan, 1 = Feb, etc.), we can simply sort
213// the items by value.
214std::vector<OUString> SortGroupItems(const ScDPCache& rCache, tools::Long nDim)
215{
216 struct ItemData
217 {
218 sal_Int32 nVal;
219 const ScDPItemData* pData;
220 };
221 std::vector<ItemData> aDataToSort;
222 ScfInt32Vec aGIIds;
223 rCache.GetGroupDimMemberIds(nDim, aGIIds);
224 for (sal_Int32 id : aGIIds)
225 {
226 const ScDPItemData* pGIData = rCache.GetItemDataById(nDim, id);
227 if (pGIData->GetType() == ScDPItemData::GroupValue)
228 {
229 auto aGroupVal = pGIData->GetGroupValue();
230 aDataToSort.push_back({ aGroupVal.mnValue, pGIData });
231 }
232 }
233 std::sort(aDataToSort.begin(), aDataToSort.end(),
234 [](const ItemData& a, const ItemData& b) { return a.nVal < b.nVal; });
235
236 std::vector<OUString> aSortedResult;
237 for (const auto& el : aDataToSort)
238 {
239 aSortedResult.push_back(rCache.GetFormattedString(nDim, *el.pData, false));
240 }
241 return aSortedResult;
242}
243} // namespace
244
245void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter )
246{
247 assert(rEntry.mpCache);
248 const ScDPCache& rCache = *rEntry.mpCache;
249
250 sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream();
251
252 OUString aRelId;
253 sax_fastparser::FSHelperPtr pRecStrm = rStrm.CreateOutputStream(
254 XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter),
255 XclXmlUtils::GetStreamName(nullptr, "pivotCacheRecords", nCounter),
256 pDefStrm->getOutputStream(),
257 CREATE_XL_CONTENT_TYPE("pivotCacheRecords"),
258 CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"),
259 &aRelId);
260
261 rStrm.PushStream(pRecStrm);
262 savePivotCacheRecordsXml(rStrm, rCache);
263 rStrm.PopStream();
264
265 pDefStrm->startElement(XML_pivotCacheDefinition,
266 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
267 FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
268 FSNS(XML_r, XML_id), aRelId.toUtf8(),
269 XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()),
270 XML_createdVersion, "3"); // MS Excel 2007, tdf#112936: setting version number makes MSO to handle the pivot table differently
271
272 pDefStrm->startElement(XML_cacheSource, XML_type, "worksheet");
273
274 OUString aSheetName;
275 GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName);
276 pDefStrm->singleElement(XML_worksheetSource,
277 XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rEntry.maSrcRange),
278 XML_sheet, aSheetName.toUtf8());
279
280 pDefStrm->endElement(XML_cacheSource);
281
282 size_t nCount = rCache.GetFieldCount();
283 const size_t nGroupFieldCount = rCache.GetGroupFieldCount();
284 pDefStrm->startElement(XML_cacheFields,
285 XML_count, OString::number(static_cast<tools::Long>(nCount + nGroupFieldCount)));
286
287 auto WriteFieldGroup = [this, &rCache, pDefStrm](size_t i, size_t base) {
288 const sal_Int32 nDatePart = rCache.GetGroupType(i);
289 if (!nDatePart)
290 return;
291 OString sGroupBy;
292 switch (nDatePart)
293 {
294 case sheet::DataPilotFieldGroupBy::SECONDS:
295 sGroupBy = "seconds";
296 break;
297 case sheet::DataPilotFieldGroupBy::MINUTES:
298 sGroupBy = "minutes";
299 break;
300 case sheet::DataPilotFieldGroupBy::HOURS:
301 sGroupBy = "hours";
302 break;
303 case sheet::DataPilotFieldGroupBy::DAYS:
304 sGroupBy = "days";
305 break;
306 case sheet::DataPilotFieldGroupBy::MONTHS:
307 sGroupBy = "months";
308 break;
309 case sheet::DataPilotFieldGroupBy::QUARTERS:
310 sGroupBy = "quarters";
311 break;
312 case sheet::DataPilotFieldGroupBy::YEARS:
313 sGroupBy = "years";
314 break;
315 }
316
317 // fieldGroup element
318 pDefStrm->startElement(XML_fieldGroup, XML_base, OString::number(base));
319
320 SvNumberFormatter& rFormatter = GetFormatter();
321
322 // rangePr element
323 const ScDPNumGroupInfo* pGI = rCache.GetNumGroupInfo(i);
325 pGroupAttList->add(XML_groupBy, sGroupBy);
326 // Possible TODO: find out when to write autoStart attribute for years grouping
327 pGroupAttList->add(XML_startDate, GetExcelFormattedDate(pGI->mfStart, rFormatter).toUtf8());
328 pGroupAttList->add(XML_endDate, GetExcelFormattedDate(pGI->mfEnd, rFormatter).toUtf8());
329 if (pGI->mfStep)
330 pGroupAttList->add(XML_groupInterval, OString::number(pGI->mfStep));
331 pDefStrm->singleElement(XML_rangePr, pGroupAttList);
332
333 // groupItems element
334 auto aElemVec = SortGroupItems(rCache, i);
335 pDefStrm->startElement(XML_groupItems, XML_count, OString::number(aElemVec.size()));
336 for (const auto& sElem : aElemVec)
337 {
338 pDefStrm->singleElement(XML_s, XML_v, sElem.toUtf8());
339 }
340 pDefStrm->endElement(XML_groupItems);
341 pDefStrm->endElement(XML_fieldGroup);
342 };
343
344 for (size_t i = 0; i < nCount; ++i)
345 {
346 OUString aName = rCache.GetDimensionName(i);
347
348 pDefStrm->startElement(XML_cacheField,
349 XML_name, aName.toUtf8(),
350 XML_numFmtId, OString::number(0));
351
352 const ScDPCache::ScDPItemDataVec& rFieldItems = rCache.GetDimMemberValues(i);
353
354 std::set<ScDPItemData::Type> aDPTypes;
355 double fMin = std::numeric_limits<double>::infinity(), fMax = -std::numeric_limits<double>::infinity();
356 bool isValueInteger = true;
357 bool isContainsDate = rCache.IsDateDimension(i);
358 bool isLongText = false;
359 for (const auto& rFieldItem : rFieldItems)
360 {
361 ScDPItemData::Type eType = rFieldItem.GetType();
362 // tdf#123939 : error and string are same for cache; if both are present, keep only one
365 aDPTypes.insert(eType);
367 {
368 double fVal = rFieldItem.GetValue();
369 fMin = std::min(fMin, fVal);
370 fMax = std::max(fMax, fVal);
371
372 // Check if all values are integers
373 if (isValueInteger && (modf(fVal, &o3tl::temporary(double())) != 0.0))
374 {
375 isValueInteger = false;
376 }
377 }
378 else if (eType == ScDPItemData::String && !isLongText)
379 {
380 isLongText = rFieldItem.GetString().getLength() > 255;
381 }
382 }
383
385 // TODO In same cases, disable listing of items, as it is done in MS Excel.
386 // Exporting savePivotCacheRecordsXml method needs to be updated accordingly
387 //bool bListItems = true;
388
389 std::set<ScDPItemData::Type> aDPTypesWithoutBlank = aDPTypes;
390 aDPTypesWithoutBlank.erase(ScDPItemData::Empty);
391
392 const bool isContainsString = aDPTypesWithoutBlank.count(ScDPItemData::String) > 0;
393 const bool isContainsBlank = aDPTypes.count(ScDPItemData::Empty) > 0;
394 const bool isContainsNumber
395 = !isContainsDate && aDPTypesWithoutBlank.count(ScDPItemData::Value) > 0;
396 bool isContainsNonDate = !(isContainsDate && aDPTypesWithoutBlank.size() <= 1);
397
398 // XML_containsSemiMixedTypes possible values:
399 // 1 - (Default) at least one text value, or can also contain a mix of other data types and blank values,
400 // or blank values only
401 // 0 - the field does not have a mix of text and other values
402 if (!(isContainsString || (aDPTypes.size() > 1) || (isContainsBlank && aDPTypesWithoutBlank.empty())))
403 pAttList->add(XML_containsSemiMixedTypes, ToPsz10(false));
404
405 if (!isContainsNonDate)
406 pAttList->add(XML_containsNonDate, ToPsz10(false));
407
408 if (isContainsDate)
409 pAttList->add(XML_containsDate, ToPsz10(true));
410
411 // default for containsString field is true, so we are writing only when is false
412 if (!isContainsString)
413 pAttList->add(XML_containsString, ToPsz10(false));
414
415 if (isContainsBlank)
416 pAttList->add(XML_containsBlank, ToPsz10(true));
417
418 // XML_containsMixedType possible values:
419 // 1 - field contains more than one data type
420 // 0 - (Default) only one data type. The field can still contain blank values (that's why we are using aDPTypesWithoutBlank)
421 if (aDPTypesWithoutBlank.size() > 1)
422 pAttList->add(XML_containsMixedTypes, ToPsz10(true));
423
424 // If field contain mixed types (Date and Numbers), MS Excel is saving only "minDate" and "maxDate" and not "minValue" and "maxValue"
425 // Example how Excel is saving mixed Date and Numbers:
426 // <sharedItems containsSemiMixedTypes="0" containsDate="1" containsString="0" containsMixedTypes="1" minDate="1900-01-03T22:26:04" maxDate="1900-01-07T14:02:04" />
427 // Example how Excel is saving Dates only:
428 // <sharedItems containsSemiMixedTypes="0" containsNonDate="0" containsDate="1" containsString="0" minDate="1903-08-24T07:40:48" maxDate="2024-05-23T07:12:00"/>
429 if (isContainsNumber)
430 pAttList->add(XML_containsNumber, ToPsz10(true));
431
432 if (isValueInteger && isContainsNumber)
433 pAttList->add(XML_containsInteger, ToPsz10(true));
434
435
436 // Number type fields could be mixed with blank types, and it shouldn't be treated as listed items.
437 // Example:
438 // <cacheField name="employeeID" numFmtId="0">
439 // <sharedItems containsString="0" containsBlank="1" containsNumber="1" containsInteger="1" minValue="35" maxValue="89"/>
440 // </cacheField>
441 if (isContainsNumber)
442 {
443 pAttList->add(XML_minValue, OString::number(fMin));
444 pAttList->add(XML_maxValue, OString::number(fMax));
445 }
446
447 if (isContainsDate)
448 {
449 pAttList->add(XML_minDate, GetExcelFormattedDate(fMin, GetFormatter()).toUtf8());
450 pAttList->add(XML_maxDate, GetExcelFormattedDate(fMax, GetFormatter()).toUtf8());
451 }
452
453 //if (bListItems) // see TODO above
454 {
455 pAttList->add(XML_count, OString::number(static_cast<tools::Long>(rFieldItems.size())));
456 }
457
458 if (isLongText)
459 {
460 pAttList->add(XML_longText, ToPsz10(true));
461 }
462
463 pDefStrm->startElement(XML_sharedItems, pAttList);
464
465 //if (bListItems) // see TODO above
466 {
467 for (const ScDPItemData& rItem : rFieldItems)
468 {
469 switch (rItem.GetType())
470 {
472 pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
473 break;
475 if (isContainsDate)
476 {
477 pDefStrm->singleElement(XML_d,
478 XML_v, GetExcelFormattedDate(rItem.GetValue(), GetFormatter()).toUtf8());
479 }
480 else
481 pDefStrm->singleElement(XML_n,
482 XML_v, OString::number(rItem.GetValue()));
483 break;
485 pDefStrm->singleElement(XML_m);
486 break;
488 pDefStrm->singleElement(XML_e,
489 XML_v, rItem.GetString().toUtf8());
490 break;
491 case ScDPItemData::GroupValue: // Should not happen here!
493 // TODO : What do we do with these types?
494 pDefStrm->singleElement(XML_m);
495 break;
496 default:
497 ;
498 }
499 }
500 }
501
502 pDefStrm->endElement(XML_sharedItems);
503
504 WriteFieldGroup(i, i);
505
506 pDefStrm->endElement(XML_cacheField);
507 }
508
509 ScDPObject* pDPObject
510 = rCache.GetAllReferences().empty() ? nullptr : *rCache.GetAllReferences().begin();
511
512 for (size_t i = nCount; pDPObject && i < nCount + nGroupFieldCount; ++i)
513 {
514 const OUString aName = pDPObject->GetDimName(i, o3tl::temporary(bool()));
515 // tdf#126748: DPObject might not reference all group fields, when there are several
516 // DPObjects referencing this cache. Trying to get a dimension data for a field not used
517 // in a given DPObject will give nullptr, and dereferencing it then will crash. To avoid
518 // the crash, until there's a correct method to find the names of group fields in cache,
519 // just skip the fields, creating bad cache data, which is of course a temporary hack.
520 // TODO: reimplement the whole block to get the names from another source, not from first
521 // cache reference.
522 if (aName.isEmpty())
523 break;
524
525 ScDPSaveData* pSaveData = pDPObject->GetSaveData();
526 assert(pSaveData);
527
529 assert(pDim);
530
531 const SCCOL nBase = rCache.GetDimensionIndex(pDim->GetSourceDimName());
532 assert(nBase >= 0);
533
534 pDefStrm->startElement(XML_cacheField, XML_name, aName.toUtf8(), XML_numFmtId,
535 OString::number(0), XML_databaseField, ToPsz10(false));
536 WriteFieldGroup(i, nBase);
537 pDefStrm->endElement(XML_cacheField);
538 }
539
540 pDefStrm->endElement(XML_cacheFields);
541
542 pDefStrm->endElement(XML_pivotCacheDefinition);
543}
544
546 XclExpRoot(rRoot), maCaches(rRoot) {}
547
549{
550 ScDocument& rDoc = GetDoc();
551 if (!rDoc.HasPivotTable())
552 // No pivot table to export.
553 return;
554
555 ScDPCollection* pDPColl = rDoc.GetDPCollection();
556 if (!pDPColl)
557 return;
558
559 // Update caches from DPObject
560 for (size_t i = 0; i < pDPColl->GetCount(); ++i)
561 {
562 ScDPObject& rDPObj = (*pDPColl)[i];
565 }
566
567 // Go through the caches first.
568
569 std::vector<XclExpXmlPivotCaches::Entry> aCaches;
570 const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches();
571 const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges();
572 for (const auto & rRange : rRanges)
573 {
574 const ScDPCache* pCache = rSheetCaches.getExistingCache(rRange);
575 if (!pCache)
576 continue;
577
578 // Get all pivot objects that reference this cache, and set up an
579 // object to cache ID mapping.
580 const ScDPCache::ScDPObjectSet& rRefs = pCache->GetAllReferences();
581 for (const auto& rRef : rRefs)
582 maCacheIdMap.emplace(rRef, aCaches.size()+1);
583
585 aEntry.mpCache = pCache;
586 aEntry.maSrcRange = rRange;
587 aCaches.push_back(aEntry); // Cache ID equals position + 1.
588 }
589
590 // TODO : Handle name and database caches as well.
591
592 for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i)
593 {
594 const ScDPObject& rDPObj = (*pDPColl)[i];
595
596 // Get the cache ID for this pivot table.
597 CacheIdMapType::iterator itCache = maCacheIdMap.find(&rDPObj);
598 if (itCache == maCacheIdMap.end())
599 // No cache ID found. Something is wrong here...
600 continue;
601
602 sal_Int32 nCacheId = itCache->second;
603 SCTAB nTab = rDPObj.GetOutRange().aStart.Tab();
604
605 TablesType::iterator it = m_Tables.find(nTab);
606 if (it == m_Tables.end())
607 {
608 // Insert a new instance for this sheet index.
609 std::pair<TablesType::iterator, bool> r =
610 m_Tables.insert(std::make_pair(nTab, std::make_unique<XclExpXmlPivotTables>(GetRoot(), maCaches)));
611 it = r.first;
612 }
613
614 XclExpXmlPivotTables *const p = it->second.get();
615 p->AppendTable(&rDPObj, nCacheId, i+1);
616 }
617
618 maCaches.SetCaches(std::move(aCaches));
619}
620
622{
623 return maCaches;
624}
625
627{
628 TablesType::iterator const it = m_Tables.find(nTab);
629 return it == m_Tables.end() ? nullptr : it->second.get();
630}
631
632XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) :
633 mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {}
634
636 XclExpRoot(rRoot), mrCaches(rCaches) {}
637
639{
640 sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream
641
642 for (const auto& rTable : maTables)
643 {
644 const ScDPObject& rObj = *rTable.mpTable;
645 sal_Int32 nCacheId = rTable.mnCacheId;
646 sal_Int32 nPivotId = rTable.mnPivotId;
647
648 sax_fastparser::FSHelperPtr pPivotStrm = rStrm.CreateOutputStream(
649 XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId),
650 XclXmlUtils::GetStreamName(nullptr, "../pivotTables/pivotTable", nPivotId),
651 pWSStrm->getOutputStream(),
652 CREATE_XL_CONTENT_TYPE("pivotTable"),
653 CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"));
654
655 rStrm.PushStream(pPivotStrm);
656 SavePivotTableXml(rStrm, rObj, nCacheId);
657 rStrm.PopStream();
658 }
659}
660
661namespace {
662
663struct DataField
664{
665 tools::Long mnPos; // field index in pivot cache.
666 const ScDPSaveDimension* mpDim;
667
668 DataField( tools::Long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
669};
670
672OString GetSubtotalFuncName(ScGeneralFunction eFunc)
673{
674 switch (eFunc)
675 {
676 case ScGeneralFunction::SUM: return "sum";
677 case ScGeneralFunction::COUNT: return "count";
678 case ScGeneralFunction::AVERAGE: return "avg";
679 case ScGeneralFunction::MAX: return "max";
680 case ScGeneralFunction::MIN: return "min";
681 case ScGeneralFunction::PRODUCT: return "product";
682 case ScGeneralFunction::COUNTNUMS: return "countA";
683 case ScGeneralFunction::STDEV: return "stdDev";
684 case ScGeneralFunction::STDEVP: return "stdDevP";
685 case ScGeneralFunction::VAR: return "var";
686 case ScGeneralFunction::VARP: return "varP";
687 default:;
688 }
689 return "default";
690}
691
692sal_Int32 GetSubtotalAttrToken(ScGeneralFunction eFunc)
693{
694 switch (eFunc)
695 {
696 case ScGeneralFunction::SUM: return XML_sumSubtotal;
697 case ScGeneralFunction::COUNT: return XML_countSubtotal;
698 case ScGeneralFunction::AVERAGE: return XML_avgSubtotal;
699 case ScGeneralFunction::MAX: return XML_maxSubtotal;
700 case ScGeneralFunction::MIN: return XML_minSubtotal;
701 case ScGeneralFunction::PRODUCT: return XML_productSubtotal;
702 case ScGeneralFunction::COUNTNUMS: return XML_countASubtotal;
703 case ScGeneralFunction::STDEV: return XML_stdDevSubtotal;
704 case ScGeneralFunction::STDEVP: return XML_stdDevPSubtotal;
705 case ScGeneralFunction::VAR: return XML_varSubtotal;
706 case ScGeneralFunction::VARP: return XML_varPSubtotal;
707 default:;
708 }
709 return XML_defaultSubtotal;
710}
711
712// An item is expected to contain sequences of css::xml::FastAttribute and css::xml::Attribute
713void WriteGrabBagItemToStream(XclExpXmlStream& rStrm, sal_Int32 tokenId, const css::uno::Any& rItem)
714{
715 css::uno::Sequence<css::uno::Any> aSeqs;
716 if(!(rItem >>= aSeqs))
717 return;
718
719 auto& pStrm = rStrm.GetCurrentStream();
720 pStrm->write("<")->writeId(tokenId);
721
722 css::uno::Sequence<css::xml::FastAttribute> aFastSeq;
723 css::uno::Sequence<css::xml::Attribute> aUnkSeq;
724 for (const auto& a : std::as_const(aSeqs))
725 {
726 if (a >>= aFastSeq)
727 {
728 for (const auto& rAttr : std::as_const(aFastSeq))
729 rStrm.WriteAttributes(rAttr.Token, rAttr.Value);
730 }
731 else if (a >>= aUnkSeq)
732 {
733 for (const auto& rAttr : std::as_const(aUnkSeq))
734 pStrm->write(" ")
735 ->write(rAttr.Name)
736 ->write("=\"")
737 ->writeEscaped(rAttr.Value)
738 ->write("\"");
739 }
740 }
741
742 pStrm->write("/>");
743}
744}
745
746void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId )
747{
748 typedef std::unordered_map<OUString, long> NameToIdMapType;
749
750 const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId);
751 if (!pCacheEntry)
752 // Something is horribly wrong. Check your logic.
753 return;
754
755 const ScDPCache& rCache = *pCacheEntry->mpCache;
756
757 const ScDPSaveData& rSaveData = *rDPObj.GetSaveData();
758
759 size_t nFieldCount = rCache.GetFieldCount() + rCache.GetGroupFieldCount();
760 std::vector<const ScDPSaveDimension*> aCachedDims;
761 NameToIdMapType aNameToIdMap;
762
763 aCachedDims.reserve(nFieldCount);
764 for (size_t i = 0; i < nFieldCount; ++i)
765 {
766 OUString aName = const_cast<ScDPObject&>(rDPObj).GetDimName(i, o3tl::temporary(bool()));
767 aNameToIdMap.emplace(aName, aCachedDims.size());
768 const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName);
769 aCachedDims.push_back(pDim);
770 }
771
772 std::vector<tools::Long> aRowFields;
773 std::vector<tools::Long> aColFields;
774 std::vector<tools::Long> aPageFields;
775 std::vector<DataField> aDataFields;
776
777 tools::Long nDataDimCount = rSaveData.GetDataDimensionCount();
778 // Use dimensions in the save data to get their correct ordering.
779 // Dimension order here is significant as they specify the order of
780 // appearance in each axis.
781 const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
782 bool bTabularMode = false;
783 bool bCompactMode = true;
784 for (const auto & i : rDims)
785 {
786 const ScDPSaveDimension& rDim = *i;
787
788 tools::Long nPos = -1; // position in cache
789 if (rDim.IsDataLayout())
790 nPos = -2; // Excel uses an index of -2 to indicate a data layout field.
791 else
792 {
793 OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName());
794 NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName);
795 if (it != aNameToIdMap.end())
796 nPos = it->second;
797
798 if (nPos == -1)
799 continue;
800
801 if (!aCachedDims[nPos])
802 continue;
803 }
804
805 sheet::DataPilotFieldOrientation eOrient = rDim.GetOrientation();
806
807 switch (eOrient)
808 {
809 case sheet::DataPilotFieldOrientation_COLUMN:
810 if (nPos == -2 && nDataDimCount <= 1)
811 break;
812 aColFields.push_back(nPos);
813 break;
814 case sheet::DataPilotFieldOrientation_ROW:
815 aRowFields.push_back(nPos);
816 break;
817 case sheet::DataPilotFieldOrientation_PAGE:
818 aPageFields.push_back(nPos);
819 break;
820 case sheet::DataPilotFieldOrientation_DATA:
821 aDataFields.emplace_back(nPos, &rDim);
822 break;
823 case sheet::DataPilotFieldOrientation_HIDDEN:
824 default:
825 ;
826 }
827 if(rDim.GetLayoutInfo())
828 {
829 const auto eLayoutMode = rDim.GetLayoutInfo()->LayoutMode;
830 bTabularMode |= (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
831 bCompactMode &= (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
832 }
833 }
834
835 sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
836 pPivotStrm->startElement(XML_pivotTableDefinition,
837 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
838 XML_name, rDPObj.GetName().toUtf8(),
839 XML_cacheId, OString::number(nCacheId),
840 XML_applyNumberFormats, ToPsz10(false),
841 XML_applyBorderFormats, ToPsz10(false),
842 XML_applyFontFormats, ToPsz10(false),
843 XML_applyPatternFormats, ToPsz10(false),
844 XML_applyAlignmentFormats, ToPsz10(false),
845 XML_applyWidthHeightFormats, ToPsz10(false),
846 XML_dataCaption, "Values",
847 XML_showDrill, ToPsz10(rSaveData.GetExpandCollapse()),
848 XML_useAutoFormatting, ToPsz10(false),
849 XML_itemPrintTitles, ToPsz10(true),
850 XML_indent, ToPsz10(false),
851 XML_outline, ToPsz10(!bTabularMode),
852 XML_outlineData, ToPsz10(!bTabularMode),
853 XML_compact, ToPsz10(bCompactMode),
854 XML_compactData, ToPsz10(bCompactMode));
855
856 // NB: Excel's range does not include page field area (if any).
858
859 sal_Int32 nFirstHeaderRow = rDPObj.GetHeaderLayout() ? 2 : 1;
860 sal_Int32 nFirstDataRow = 2;
861 sal_Int32 nFirstDataCol = 1;
862 ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT);
863
864 if (!aOutRange.IsValid())
865 aOutRange = rDPObj.GetOutRange();
866
867 if (aOutRange.IsValid() && aResRange.IsValid())
868 {
869 nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row();
870 nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col();
871 }
872
873 pPivotStrm->write("<")->writeId(XML_location);
874 rStrm.WriteAttributes(XML_ref,
875 XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aOutRange),
876 XML_firstHeaderRow, OUString::number(nFirstHeaderRow),
877 XML_firstDataRow, OUString::number(nFirstDataRow),
878 XML_firstDataCol, OUString::number(nFirstDataCol));
879
880 if (!aPageFields.empty())
881 {
882 rStrm.WriteAttributes(XML_rowPageCount, OUString::number(static_cast<tools::Long>(aPageFields.size())));
883 rStrm.WriteAttributes(XML_colPageCount, OUString::number(1));
884 }
885
886 pPivotStrm->write("/>");
887
888 // <pivotFields> - It must contain all fields in the pivot cache even if
889 // only some of them are used in the pivot table. The order must be as
890 // they appear in the cache.
891
892 pPivotStrm->startElement(XML_pivotFields,
893 XML_count, OString::number(static_cast<tools::Long>(aCachedDims.size())));
894
895 for (size_t i = 0; i < nFieldCount; ++i)
896 {
897 const ScDPSaveDimension* pDim = aCachedDims[i];
898 if (!pDim)
899 {
900 pPivotStrm->singleElement(XML_pivotField,
901 XML_compact, ToPsz10(false),
902 XML_showAll, ToPsz10(false));
903 continue;
904 }
905
906 bool bDimInTabularMode = false;
907 bool bDimInCompactMode = false;
908 if(pDim->GetLayoutInfo())
909 {
910 const auto eLayoutMode = pDim->GetLayoutInfo()->LayoutMode;
911 bDimInTabularMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
912 bDimInCompactMode = (eLayoutMode == sheet::DataPilotFieldLayoutMode::COMPACT_LAYOUT);
913 }
914
915 sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
916
917 if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN)
918 {
919 if(bDimInTabularMode)
920 {
921 pPivotStrm->singleElement(XML_pivotField,
922 XML_compact, ToPsz10(false),
923 XML_showAll, ToPsz10(false),
924 XML_outline, ToPsz10(false));
925 }
926 else
927 {
928 if (bDimInCompactMode)
929 pPivotStrm->singleElement(XML_pivotField,
930 XML_showAll, ToPsz10(false));
931 else
932 pPivotStrm->singleElement(XML_pivotField,
933 XML_compact, ToPsz10(false),
934 XML_showAll, ToPsz10(false));
935 }
936 continue;
937 }
938
939 if (eOrient == sheet::DataPilotFieldOrientation_DATA)
940 {
941 if(bDimInTabularMode)
942 {
943 pPivotStrm->singleElement(XML_pivotField,
944 XML_dataField, ToPsz10(true),
945 XML_compact, ToPsz10(false),
946 XML_showAll, ToPsz10(false),
947 XML_outline, ToPsz10(false));
948 }
949 else
950 {
951 if (bDimInCompactMode)
952 pPivotStrm->singleElement(XML_pivotField,
953 XML_dataField, ToPsz10(true),
954 XML_showAll, ToPsz10(false));
955 else
956 pPivotStrm->singleElement(XML_pivotField,
957 XML_dataField, ToPsz10(true),
958 XML_compact, ToPsz10(false),
959 XML_showAll, ToPsz10(false));
960 }
961 continue;
962 }
963
964 // Dump field items.
965 std::vector<ScDPLabelData::Member> aMembers;
966 {
967 // We need to get the members in actual order, getting which requires non-const reference here
968 auto& dpo = const_cast<ScDPObject&>(rDPObj);
969 dpo.GetMembers(i, dpo.GetUsedHierarchy(i), aMembers);
970 }
971
972 std::vector<OUString> aCacheFieldItems;
973 if (i < rCache.GetFieldCount() && !rCache.GetGroupType(i))
974 {
975 for (const auto& it : rCache.GetDimMemberValues(i))
976 {
977 OUString sFormattedName;
978 if (it.HasStringData() || it.IsEmpty())
979 sFormattedName = it.GetString();
980 else
981 sFormattedName = const_cast<ScDPObject&>(rDPObj).GetFormattedString(
982 pDim->GetName(), it.GetValue());
983 aCacheFieldItems.push_back(sFormattedName);
984 }
985 }
986 else
987 {
988 aCacheFieldItems = SortGroupItems(rCache, i);
989 }
990 // The pair contains the member index in cache and if it is hidden
991 std::vector< std::pair<size_t, bool> > aMemberSequence;
992 std::set<size_t> aUsedCachePositions;
993 for (const auto & rMember : aMembers)
994 {
995 auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
996 if (it != aCacheFieldItems.end())
997 {
998 size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
999 auto aInserted = aUsedCachePositions.insert(nCachePos);
1000 if (aInserted.second)
1001 aMemberSequence.emplace_back(std::make_pair(nCachePos, !rMember.mbVisible));
1002 }
1003 }
1004 // Now add all remaining cache items as hidden
1005 for (size_t nItem = 0; nItem < aCacheFieldItems.size(); ++nItem)
1006 {
1007 if (aUsedCachePositions.find(nItem) == aUsedCachePositions.end())
1008 aMemberSequence.emplace_back(nItem, true);
1009 }
1010
1011 // tdf#125086: check if this field *also* appears in Data region
1012 bool bAppearsInData = false;
1013 {
1014 OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
1015 const auto it = std::find_if(
1016 aDataFields.begin(), aDataFields.end(), [&aSrcName](const DataField& rDataField) {
1017 OUString aThisName
1018 = ScDPUtil::getSourceDimensionName(rDataField.mpDim->GetName());
1019 return aThisName == aSrcName;
1020 });
1021 if (it != aDataFields.end())
1022 bAppearsInData = true;
1023 }
1024
1026 pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
1027 if (bAppearsInData)
1028 pAttList->add(XML_dataField, ToPsz10(true));
1029
1030 if (!bDimInCompactMode)
1031 pAttList->add(XML_compact, ToPsz10(false));
1032
1033 pAttList->add(XML_showAll, ToPsz10(false));
1034
1035 tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
1036 std::vector<OString> aSubtotalSequence;
1037 bool bHasDefaultSubtotal = false;
1038 for (tools::Long nSubTotal = 0; nSubTotal < nSubTotalCount; ++nSubTotal)
1039 {
1040 ScGeneralFunction eFunc = pDim->GetSubTotalFunc(nSubTotal);
1041 aSubtotalSequence.push_back(GetSubtotalFuncName(eFunc));
1042 sal_Int32 nAttToken = GetSubtotalAttrToken(eFunc);
1043 if (nAttToken == XML_defaultSubtotal)
1044 bHasDefaultSubtotal = true;
1045 else if (!pAttList->hasAttribute(nAttToken))
1046 pAttList->add(nAttToken, ToPsz10(true));
1047 }
1048 // XML_defaultSubtotal is true by default; only write it if it's false
1049 if (!bHasDefaultSubtotal)
1050 pAttList->add(XML_defaultSubtotal, ToPsz10(false));
1051
1052 if(bDimInTabularMode)
1053 pAttList->add( XML_outline, ToPsz10(false));
1054 pPivotStrm->startElement(XML_pivotField, pAttList);
1055
1056 pPivotStrm->startElement(XML_items,
1057 XML_count, OString::number(static_cast<tools::Long>(aMemberSequence.size() + aSubtotalSequence.size())));
1058
1059 for (const auto & nMember : aMemberSequence)
1060 {
1062 if (nMember.second)
1063 pItemAttList->add(XML_h, ToPsz10(true));
1064 pItemAttList->add(XML_x, OString::number(static_cast<tools::Long>(nMember.first)));
1065 pPivotStrm->singleElement(XML_item, pItemAttList);
1066 }
1067
1068 for (const OString& sSubtotal : aSubtotalSequence)
1069 {
1070 pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
1071 }
1072
1073 pPivotStrm->endElement(XML_items);
1074 pPivotStrm->endElement(XML_pivotField);
1075 }
1076
1077 pPivotStrm->endElement(XML_pivotFields);
1078
1079 // <rowFields>
1080
1081 if (!aRowFields.empty())
1082 {
1083 pPivotStrm->startElement(XML_rowFields,
1084 XML_count, OString::number(static_cast<tools::Long>(aRowFields.size())));
1085
1086 for (const auto& rRowField : aRowFields)
1087 {
1088 pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
1089 }
1090
1091 pPivotStrm->endElement(XML_rowFields);
1092 }
1093
1094 // <rowItems>
1095
1096 // <colFields>
1097
1098 if (!aColFields.empty())
1099 {
1100 pPivotStrm->startElement(XML_colFields,
1101 XML_count, OString::number(static_cast<tools::Long>(aColFields.size())));
1102
1103 for (const auto& rColField : aColFields)
1104 {
1105 pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
1106 }
1107
1108 pPivotStrm->endElement(XML_colFields);
1109 }
1110
1111 // <colItems>
1112
1113 // <pageFields>
1114
1115 if (!aPageFields.empty())
1116 {
1117 pPivotStrm->startElement(XML_pageFields,
1118 XML_count, OString::number(static_cast<tools::Long>(aPageFields.size())));
1119
1120 for (const auto& rPageField : aPageFields)
1121 {
1122 pPivotStrm->singleElement(XML_pageField,
1123 XML_fld, OString::number(rPageField),
1124 XML_hier, OString::number(-1)); // TODO : handle this correctly.
1125 }
1126
1127 pPivotStrm->endElement(XML_pageFields);
1128 }
1129
1130 // <dataFields>
1131
1132 if (!aDataFields.empty())
1133 {
1134 css::uno::Reference<css::container::XNameAccess> xDimsByName;
1135 if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
1136 xDimsByName = xDimSupplier->getDimensions();
1137
1138 pPivotStrm->startElement(XML_dataFields,
1139 XML_count, OString::number(static_cast<tools::Long>(aDataFields.size())));
1140
1141 for (const auto& rDataField : aDataFields)
1142 {
1143 tools::Long nDimIdx = rDataField.mnPos;
1144 assert(nDimIdx == -2 || aCachedDims[nDimIdx]); // the loop above should have screened for NULL's, skip check for -2 "data field"
1145 const ScDPSaveDimension& rDim = *rDataField.mpDim;
1146 std::optional<OUString> pName = rDim.GetLayoutName();
1147 // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
1148 // Excel (at least 2016) seems to insist on the presence of "name" attribute in
1149 // dataField element.
1150 // tdf#124881: try to create a meaningful name; don't use empty string.
1151 if (!pName)
1155 pItemAttList->add(XML_name, pName->toUtf8());
1156 pItemAttList->add(XML_fld, OString::number(nDimIdx));
1157 const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
1158 if (pSubtotal)
1159 pItemAttList->add(XML_subtotal, pSubtotal);
1160 if (xDimsByName)
1161 {
1162 try
1163 {
1164 css::uno::Reference<css::beans::XPropertySet> xDimProps(
1165 xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
1166 css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
1167 sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
1168 sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
1169 pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
1170 }
1171 catch (uno::Exception&)
1172 {
1173 SAL_WARN("sc.filter",
1174 "Couldn't get number format for data field " << rDim.GetName());
1175 // Just skip exporting number format
1176 }
1177 }
1178 pPivotStrm->singleElement(XML_dataField, pItemAttList);
1179 }
1180
1181 pPivotStrm->endElement(XML_dataFields);
1182 }
1183
1184 // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
1185 if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
1186 WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
1187 else
1188 pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
1189 XML_showRowHeaders, "1", XML_showColHeaders, "1",
1190 XML_showRowStripes, "0", XML_showColStripes, "0",
1191 XML_showLastColumn, "1");
1192
1193 OUString aBuf = "../pivotCache/pivotCacheDefinition" +
1194 OUString::number(nCacheId) +
1195 ".xml";
1196
1197 rStrm.addRelation(
1198 pPivotStrm->getOutputStream(),
1199 CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
1200 aBuf);
1201
1202 pPivotStrm->endElement(XML_pivotTableDefinition);
1203}
1204
1205void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
1206{
1207 maTables.emplace_back(pTable, nCacheId, nPivotId);
1208}
1209
1210/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const char * pName
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:48
sal_Int32 GetGroupType(tools::Long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1389
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data.
Definition: dpcache.cxx:1036
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:983
bool IsDateDimension(tools::Long nDim) const
Definition: dpcache.cxx:1066
std::vector< SCROW > IndexArrayType
Definition: dpcache.hxx:54
void GetGroupDimMemberIds(tools::Long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1317
OUString GetFormattedString(tools::Long nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dpcache.cxx:1205
const ScDPNumGroupInfo * GetNumGroupInfo(tools::Long nDim) const
Definition: dpcache.cxx:1368
const ScDPObjectSet & GetAllReferences() const
Definition: dpcache.cxx:1117
size_t GetGroupFieldCount() const
Definition: dpcache.cxx:1026
size_t GetFieldCount() const
Definition: dpcache.cxx:1021
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:916
const ScDPItemDataVec & GetDimMemberValues(SCCOL nDim) const
Definition: dpcache.cxx:1050
std::vector< ScDPItemData > ScDPItemDataVec
Definition: dpcache.hxx:52
const IndexArrayType * GetFieldIndexArray(size_t nDim) const
Definition: dpcache.cxx:1042
SCCOL GetDimensionIndex(std::u16string_view sName) const
Definition: dpcache.cxx:1085
Stores and manages all caches from internal sheets.
Definition: dpobject.hxx:292
SC_DLLPUBLIC const std::vector< ScRange > & getAllRanges() const
Definition: dpobject.cxx:3127
SC_DLLPUBLIC ScDPCache * getExistingCache(const ScRange &rRange)
Definition: dpobject.cxx:3008
SC_DLLPUBLIC SheetCaches & GetSheetCaches()
Definition: dpobject.cxx:3794
SC_DLLPUBLIC size_t GetCount() const
Definition: dpobject.cxx:3717
const ScDPSaveGroupDimension * GetNamedGroupDim(const OUString &rGroupDimName) const
Definition: dpdimsave.cxx:636
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
Type GetType() const
Definition: dpitemdata.hxx:67
GroupValueAttr GetGroupValue() const
Definition: dpitemdata.cxx:355
const ScRange & GetOutRange() const
Definition: dpobject.cxx:410
bool SyncAllDimensionMembers()
Remove in the save data entries for members that don't exist anymore.
Definition: dpobject.cxx:974
bool GetHeaderLayout() const
Definition: dpobject.hxx:147
std::pair< bool, css::uno::Any > GetInteropGrabBagValue(const OUString &sName) const
Definition: dpobject.hxx:269
bool GetMembers(sal_Int32 nDim, sal_Int32 nHier, ::std::vector< ScDPLabelData::Member > &rMembers)
Definition: dpobject.cxx:1012
ScRange GetOutputRangeByType(sal_Int32 nType)
Definition: dpobject.cxx:911
ScDPSaveData * GetSaveData() const
Definition: dpobject.hxx:141
const OUString & GetName() const
Definition: dpobject.hxx:167
OUString GetDimName(tools::Long nDim, bool &rIsDataLayout, sal_Int32 *pFlags=nullptr)
Definition: dpobject.cxx:1207
SC_DLLPUBLIC ScDPSaveDimension * GetExistingDimensionByName(std::u16string_view rName) const
Definition: dpsave.cxx:849
bool GetExpandCollapse() const
Definition: dpsave.hxx:347
std::vector< std::unique_ptr< ScDPSaveDimension > > DimsType
Definition: dpsave.hxx:238
const DimsType & GetDimensions() const
Definition: dpsave.hxx:271
SC_DLLPUBLIC ScDPDimensionSaveData * GetDimensionData()
Definition: dpsave.cxx:1204
SC_DLLPUBLIC tools::Long GetDataDimensionCount() const
Definition: dpsave.cxx:946
ScGeneralFunction GetSubTotalFunc(tools::Long nIndex) const
Definition: dpsave.hxx:152
css::sheet::DataPilotFieldOrientation GetOrientation() const
Definition: dpsave.hxx:202
const css::sheet::DataPilotFieldLayoutInfo * GetLayoutInfo() const
Definition: dpsave.hxx:194
const std::optional< OUString > & GetLayoutName() const
Definition: dpsave.cxx:386
ScGeneralFunction GetFunction() const
Definition: dpsave.hxx:165
tools::Long GetSubTotalsCount() const
Definition: dpsave.hxx:149
bool IsDataLayout() const
Definition: dpsave.hxx:142
const OUString & GetName() const
Definition: dpsave.hxx:139
Represents a new group dimension whose dimension ID is higher than the highest source dimension ID.
Definition: dpdimsave.hxx:91
const OUString & GetSourceDimName() const
Definition: dpdimsave.hxx:108
static SC_DLLPUBLIC OUString getSourceDimensionName(std::u16string_view rName)
Definition: dputil.cxx:66
static SC_DLLPUBLIC ScSubTotalFunc toSubTotalFunc(ScGeneralFunction eGenFunc)
Definition: dputil.cxx:395
static SC_DLLPUBLIC OUString getDisplayedMeasureName(const OUString &rName, ScSubTotalFunc eFunc)
Definition: dputil.cxx:384
SC_DLLPUBLIC bool HasPivotTable() const
Definition: documen3.cxx:360
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:204
SC_DLLPUBLIC ScDPCollection * GetDPCollection()
Definition: documen3.cxx:365
bool IsValid() const
Definition: address.hxx:544
ScAddress aStart
Definition: address.hxx:497
const Date & GetNullDate() const
sal_uInt16 Insert(sal_uInt32 nScNumFmt)
Inserts a number format into the format buffer.
Definition: xestyle.cxx:1359
Access to global data from other classes.
Definition: xeroot.hxx:113
XclExpNumFmtBuffer & GetNumFmtBuffer() const
Returns the number format buffer.
Definition: xeroot.cxx:118
const XclExpRoot & GetRoot() const
Returns this root instance - for code readability in derived classes.
Definition: xeroot.hxx:118
const Entry * GetCache(sal_Int32 nCacheId) const
Definition: xepivotxml.cxx:174
void SavePivotCacheXml(XclExpXmlStream &rStrm, const Entry &rEntry, sal_Int32 nCacheId)
Definition: xepivotxml.cxx:245
bool HasCaches() const
Definition: xepivotxml.cxx:169
virtual void SaveXml(XclExpXmlStream &rStrm) override
Definition: xepivotxml.cxx:133
XclExpXmlPivotCaches(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:130
void SetCaches(std::vector< Entry > &&rCaches)
Definition: xepivotxml.cxx:164
std::vector< Entry > maCaches
Definition: xepivotxml.hxx:42
XclExpXmlPivotCaches & GetCaches()
Definition: xepivotxml.cxx:621
CacheIdMapType maCacheIdMap
Definition: xepivotxml.hxx:87
XclExpXmlPivotTables * GetTablesBySheet(SCTAB nTab)
Definition: xepivotxml.cxx:626
XclExpXmlPivotCaches maCaches
Definition: xepivotxml.hxx:85
XclExpXmlPivotTableManager(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:545
XclExpXmlPivotTables(const XclExpRoot &rRoot, const XclExpXmlPivotCaches &rCaches)
Definition: xepivotxml.cxx:635
void SavePivotTableXml(XclExpXmlStream &rStrm, const ScDPObject &rObj, sal_Int32 nCacheId)
Definition: xepivotxml.cxx:746
void AppendTable(const ScDPObject *pTable, sal_Int32 nCacheId, sal_Int32 nPivotId)
const XclExpXmlPivotCaches & mrCaches
Definition: xepivotxml.hxx:56
virtual void SaveXml(XclExpXmlStream &rStrm) override
Definition: xepivotxml.cxx:638
SvNumberFormatter & GetFormatter() const
Returns the number formatter of the Calc document.
Definition: xlroot.cxx:322
ScDocument & GetDoc() const
Returns reference to the destination document (import) or source document (export).
Definition: xlroot.cxx:285
static OString ToOString(const Color &rColor)
Definition: xestream.cxx:712
static OUString GetStreamName(const char *sStreamDir, const char *sStream, sal_Int32 nId)
Definition: xestream.cxx:697
const_iterator begin() const
bool empty() const
static void convertDateTime(OUStringBuffer &rBuffer, const css::util::DateTime &rDateTime, sal_Int16 const *pTimeZoneOffset, bool bAddTimeIf0AM=false)
static rtl::Reference< FastAttributeList > createAttrList()
int nCount
DocumentType eType
constexpr sal_Int32 FSNS(sal_Int32 namespc, sal_Int32 element)
::std::vector< sal_Int32 > ScfInt32Vec
Definition: ftools.hxx:256
void const * base
ScGeneralFunction
the css::sheet::GeneralFunction enum is extended by constants in GeneralFunction2,...
@ AVERAGE
average of all numerical values is calculated.
@ PRODUCT
product of all numerical values is calculated.
@ MAX
maximum value of all numerical values is calculated.
@ COUNT
all values, including non-numerical values, are counted.
@ VARP
variance is calculated based on the entire population.
@ SUM
sum of all numerical values is calculated.
@ STDEVP
standard deviation is calculated based on the entire population.
@ COUNTNUMS
numerical values are counted.
@ MIN
minimum value of all numerical values is calculated.
@ VAR
variance is calculated based on a sample.
@ STDEV
standard deviation is calculated based on a sample.
OUString aName
void * p
sal_Int64 n
sal_uInt16 nPos
#define SAL_WARN(area, stream)
aBuf
std::unique_ptr< sal_Int32[]> pData
int i
void SvStream & rStrm
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
constexpr T & temporary(T &&x)
std::shared_ptr< FastSerializerHelper > FSHelperPtr
XML_type
long Long
#define CREATE_OFFICEDOC_RELATION_TYPE(ascii)
#define CREATE_XL_CONTENT_TYPE(ascii)
const ScDPCache * mpCache
Definition: xepivotxml.hxx:27
Entry(const ScDPObject *pTable, sal_Int32 nCacheId, sal_Int32 nPivotId)
used as [n] in pivotTable[n].xml part name.
Definition: xepivotxml.cxx:632
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
constexpr OUStringLiteral SC_UNONAME_NUMFMT
Definition: unonames.hxx:109
@ TABLE
Definition: xmldpimp.hxx:43