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