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