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<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, 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<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<long>(rFieldItems.size())));
454  }
455 
456  if (isLongText)
457  {
458  pAttList->add(XML_longText, ToPsz10(true));
459  }
460 
461  sax_fastparser::XFastAttributeListRef xAttributeList(pAttList);
462 
463  pDefStrm->startElement(XML_sharedItems, xAttributeList);
464 
465  //if (bListItems) // see TODO above
466  {
467  for (const ScDPItemData& rItem : rFieldItems)
468  {
469  switch (rItem.GetType())
470  {
472  pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
473  break;
474  case ScDPItemData::Value:
475  if (isContainsDate)
476  {
477  pDefStrm->singleElement(XML_d,
478  XML_v, GetExcelFormattedDate(rItem.GetValue(), GetFormatter()).toUtf8());
479  }
480  else
481  pDefStrm->singleElement(XML_n,
482  XML_v, OString::number(rItem.GetValue()));
483  break;
484  case ScDPItemData::Empty:
485  pDefStrm->singleElement(XML_m);
486  break;
487  case ScDPItemData::Error:
488  pDefStrm->singleElement(XML_e,
489  XML_v, rItem.GetString().toUtf8());
490  break;
491  case ScDPItemData::GroupValue: // Should not happen here!
493  // TODO : What do we do with these types?
494  pDefStrm->singleElement(XML_m);
495  break;
496  default:
497  ;
498  }
499  }
500  }
501 
502  pDefStrm->endElement(XML_sharedItems);
503 
504  WriteFieldGroup(i, i);
505 
506  pDefStrm->endElement(XML_cacheField);
507  }
508 
509  ScDPObject* pDPObject
510  = rCache.GetAllReferences().empty() ? nullptr : *rCache.GetAllReferences().begin();
511 
512  for (size_t i = nCount; pDPObject && i < nCount + nGroupFieldCount; ++i)
513  {
514  const OUString aName = pDPObject->GetDimName(i, o3tl::temporary(bool()));
515  // tdf#126748: DPObject might not reference all group fields, when there are several
516  // DPObjects referencing this cache. Trying to get a dimension data for a field not used
517  // in a given DPObject will give nullptr, and dereferencing it then will crash. To avoid
518  // the crash, until there's a correct method to find the names of group fields in cache,
519  // just skip the fields, creating bad cache data, which is of course a temporary hack.
520  // TODO: reimplement the whole block to get the names from another source, not from first
521  // cache reference.
522  if (aName.isEmpty())
523  break;
524 
525  ScDPSaveData* pSaveData = pDPObject->GetSaveData();
526  assert(pSaveData);
527 
528  const ScDPSaveGroupDimension* pDim = pSaveData->GetDimensionData()->GetNamedGroupDim(aName);
529  assert(pDim);
530 
531  const SCCOL nBase = rCache.GetDimensionIndex(pDim->GetSourceDimName());
532  assert(nBase >= 0);
533 
534  pDefStrm->startElement(XML_cacheField, XML_name, aName.toUtf8(), XML_numFmtId,
535  OString::number(0), XML_databaseField, ToPsz10(false));
536  WriteFieldGroup(i, nBase);
537  pDefStrm->endElement(XML_cacheField);
538  }
539 
540  pDefStrm->endElement(XML_cacheFields);
541 
542  pDefStrm->endElement(XML_pivotCacheDefinition);
543 }
544 
546  XclExpRoot(rRoot), maCaches(rRoot) {}
547 
549 {
550  ScDocument& rDoc = GetDoc();
551  if (!rDoc.HasPivotTable())
552  // No pivot table to export.
553  return;
554 
555  ScDPCollection* pDPColl = rDoc.GetDPCollection();
556  if (!pDPColl)
557  return;
558 
559  // Update caches from DPObject
560  for (size_t i = 0; i < pDPColl->GetCount(); ++i)
561  {
562  ScDPObject& rDPObj = (*pDPColl)[i];
563  rDPObj.SyncAllDimensionMembers();
565  }
566 
567  // Go through the caches first.
568 
569  std::vector<XclExpXmlPivotCaches::Entry> aCaches;
570  const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches();
571  const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges();
572  for (const auto & rRange : rRanges)
573  {
574  const ScDPCache* pCache = rSheetCaches.getExistingCache(rRange);
575  if (!pCache)
576  continue;
577 
578  // Get all pivot objects that reference this cache, and set up an
579  // object to cache ID mapping.
580  const ScDPCache::ScDPObjectSet& rRefs = pCache->GetAllReferences();
581  for (const auto& rRef : rRefs)
582  maCacheIdMap.emplace(rRef, aCaches.size()+1);
583 
585  aEntry.mpCache = pCache;
586  aEntry.maSrcRange = rRange;
587  aCaches.push_back(aEntry); // Cache ID equals position + 1.
588  }
589 
590  // TODO : Handle name and database caches as well.
591 
592  for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i)
593  {
594  const ScDPObject& rDPObj = (*pDPColl)[i];
595 
596  // Get the cache ID for this pivot table.
597  CacheIdMapType::iterator itCache = maCacheIdMap.find(&rDPObj);
598  if (itCache == maCacheIdMap.end())
599  // No cache ID found. Something is wrong here...
600  continue;
601 
602  sal_Int32 nCacheId = itCache->second;
603  SCTAB nTab = rDPObj.GetOutRange().aStart.Tab();
604 
605  TablesType::iterator it = m_Tables.find(nTab);
606  if (it == m_Tables.end())
607  {
608  // Insert a new instance for this sheet index.
609  std::pair<TablesType::iterator, bool> r =
610  m_Tables.insert(std::make_pair(nTab, std::make_unique<XclExpXmlPivotTables>(GetRoot(), maCaches)));
611  it = r.first;
612  }
613 
614  XclExpXmlPivotTables *const p = it->second.get();
615  p->AppendTable(&rDPObj, nCacheId, i+1);
616  }
617 
618  maCaches.SetCaches(aCaches);
619 }
620 
622 {
623  return maCaches;
624 }
625 
627 {
628  TablesType::iterator const it = m_Tables.find(nTab);
629  return it == m_Tables.end() ? nullptr : it->second.get();
630 }
631 
632 XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) :
633  mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {}
634 
636  XclExpRoot(rRoot), mrCaches(rCaches) {}
637 
639 {
640  sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream
641 
642  for (const auto& rTable : maTables)
643  {
644  const ScDPObject& rObj = *rTable.mpTable;
645  sal_Int32 nCacheId = rTable.mnCacheId;
646  sal_Int32 nPivotId = rTable.mnPivotId;
647 
649  XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId),
650  XclXmlUtils::GetStreamName(nullptr, "../pivotTables/pivotTable", nPivotId),
651  pWSStrm->getOutputStream(),
652  CREATE_XL_CONTENT_TYPE("pivotTable"),
653  CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"));
654 
655  rStrm.PushStream(pPivotStrm);
656  SavePivotTableXml(rStrm, rObj, nCacheId);
657  rStrm.PopStream();
658  }
659 }
660 
661 namespace {
662 
663 struct DataField
664 {
665  long mnPos; // field index in pivot cache.
666  const ScDPSaveDimension* mpDim;
667 
668  DataField( long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
669 };
670 
672 OString GetSubtotalFuncName(ScGeneralFunction eFunc)
673 {
674  switch (eFunc)
675  {
676  case ScGeneralFunction::SUM: return "sum";
677  case ScGeneralFunction::COUNT: return "count";
678  case ScGeneralFunction::AVERAGE: return "avg";
679  case ScGeneralFunction::MAX: return "max";
680  case ScGeneralFunction::MIN: return "min";
681  case ScGeneralFunction::PRODUCT: return "product";
682  case ScGeneralFunction::COUNTNUMS: return "countA";
683  case ScGeneralFunction::STDEV: return "stdDev";
684  case ScGeneralFunction::STDEVP: return "stdDevP";
685  case ScGeneralFunction::VAR: return "var";
686  case ScGeneralFunction::VARP: return "varP";
687  default:;
688  }
689  return "default";
690 }
691 
692 sal_Int32 GetSubtotalAttrToken(ScGeneralFunction eFunc)
693 {
694  switch (eFunc)
695  {
696  case ScGeneralFunction::SUM: return XML_sumSubtotal;
697  case ScGeneralFunction::COUNT: return XML_countSubtotal;
698  case ScGeneralFunction::AVERAGE: return XML_avgSubtotal;
699  case ScGeneralFunction::MAX: return XML_maxSubtotal;
700  case ScGeneralFunction::MIN: return XML_minSubtotal;
701  case ScGeneralFunction::PRODUCT: return XML_productSubtotal;
702  case ScGeneralFunction::COUNTNUMS: return XML_countASubtotal;
703  case ScGeneralFunction::STDEV: return XML_stdDevSubtotal;
704  case ScGeneralFunction::STDEVP: return XML_stdDevPSubtotal;
705  case ScGeneralFunction::VAR: return XML_varSubtotal;
706  case ScGeneralFunction::VARP: return XML_varPSubtotal;
707  default:;
708  }
709  return XML_defaultSubtotal;
710 }
711 
712 // An item is expected to contain sequences of css::xml::FastAttribute and css::xml::Attribute
713 void WriteGrabBagItemToStream(XclExpXmlStream& rStrm, sal_Int32 tokenId, const css::uno::Any& rItem)
714 {
715  css::uno::Sequence<css::uno::Any> aSeqs;
716  if(!(rItem >>= aSeqs))
717  return;
718 
719  auto& pStrm = rStrm.GetCurrentStream();
720  pStrm->write("<")->writeId(tokenId);
721 
722  css::uno::Sequence<css::xml::FastAttribute> aFastSeq;
723  css::uno::Sequence<css::xml::Attribute> aUnkSeq;
724  for (const auto& a : std::as_const(aSeqs))
725  {
726  if (a >>= aFastSeq)
727  {
728  for (const auto& rAttr : std::as_const(aFastSeq))
729  rStrm.WriteAttributes(rAttr.Token, rAttr.Value);
730  }
731  else if (a >>= aUnkSeq)
732  {
733  for (const auto& rAttr : std::as_const(aUnkSeq))
734  pStrm->write(" ")
735  ->write(rAttr.Name)
736  ->write("=\"")
737  ->writeEscaped(rAttr.Value)
738  ->write("\"");
739  }
740  }
741 
742  pStrm->write("/>");
743 }
744 }
745 
746 void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId )
747 {
748  typedef std::unordered_map<OUString, long> NameToIdMapType;
749 
750  const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId);
751  if (!pCacheEntry)
752  // Something is horribly wrong. Check your logic.
753  return;
754 
755  const ScDPCache& rCache = *pCacheEntry->mpCache;
756 
757  const ScDPSaveData& rSaveData = *rDPObj.GetSaveData();
758 
759  size_t nFieldCount = rCache.GetFieldCount() + rCache.GetGroupFieldCount();
760  std::vector<const ScDPSaveDimension*> aCachedDims;
761  NameToIdMapType aNameToIdMap;
762 
763  aCachedDims.reserve(nFieldCount);
764  for (size_t i = 0; i < nFieldCount; ++i)
765  {
766  OUString aName = const_cast<ScDPObject&>(rDPObj).GetDimName(i, o3tl::temporary(bool()));
767  aNameToIdMap.emplace(aName, aCachedDims.size());
768  const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName);
769  aCachedDims.push_back(pDim);
770  }
771 
772  std::vector<long> aRowFields;
773  std::vector<long> aColFields;
774  std::vector<long> aPageFields;
775  std::vector<DataField> aDataFields;
776 
777  long nDataDimCount = rSaveData.GetDataDimensionCount();
778  // Use dimensions in the save data to get their correct ordering.
779  // Dimension order here is significant as they specify the order of
780  // appearance in each axis.
781  const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
782  bool bTabularMode = false;
783  for (const auto & i : rDims)
784  {
785  const ScDPSaveDimension& rDim = *i;
786 
787  long nPos = -1; // position in cache
788  if (rDim.IsDataLayout())
789  nPos = -2; // Excel uses an index of -2 to indicate a data layout field.
790  else
791  {
792  OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName());
793  NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName);
794  if (it != aNameToIdMap.end())
795  nPos = it->second;
796 
797  if (nPos == -1)
798  continue;
799 
800  if (!aCachedDims[nPos])
801  continue;
802  }
803 
804  sheet::DataPilotFieldOrientation eOrient = rDim.GetOrientation();
805 
806  switch (eOrient)
807  {
808  case sheet::DataPilotFieldOrientation_COLUMN:
809  if (nPos == -2 && nDataDimCount <= 1)
810  break;
811  aColFields.push_back(nPos);
812  break;
813  case sheet::DataPilotFieldOrientation_ROW:
814  aRowFields.push_back(nPos);
815  break;
816  case sheet::DataPilotFieldOrientation_PAGE:
817  aPageFields.push_back(nPos);
818  break;
819  case sheet::DataPilotFieldOrientation_DATA:
820  aDataFields.emplace_back(nPos, &rDim);
821  break;
822  case sheet::DataPilotFieldOrientation_HIDDEN:
823  default:
824  ;
825  }
826  if(rDim.GetLayoutInfo())
827  bTabularMode |= (rDim.GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
828  }
829 
830  sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
831  pPivotStrm->startElement(XML_pivotTableDefinition,
832  XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
833  XML_name, rDPObj.GetName().toUtf8(),
834  XML_cacheId, OString::number(nCacheId),
835  XML_applyNumberFormats, ToPsz10(false),
836  XML_applyBorderFormats, ToPsz10(false),
837  XML_applyFontFormats, ToPsz10(false),
838  XML_applyPatternFormats, ToPsz10(false),
839  XML_applyAlignmentFormats, ToPsz10(false),
840  XML_applyWidthHeightFormats, ToPsz10(false),
841  XML_dataCaption, "Values",
842  XML_useAutoFormatting, ToPsz10(false),
843  XML_itemPrintTitles, ToPsz10(true),
844  XML_indent, ToPsz10(false),
845  XML_outline, ToPsz10(!bTabularMode),
846  XML_outlineData, ToPsz10(!bTabularMode),
847  XML_compact, ToPsz10(false),
848  XML_compactData, ToPsz10(false));
849 
850  // NB: Excel's range does not include page field area (if any).
852 
853  sal_Int32 nFirstHeaderRow = rDPObj.GetHeaderLayout() ? 2 : 1;
854  sal_Int32 nFirstDataRow = 2;
855  sal_Int32 nFirstDataCol = 1;
856  ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT);
857 
858  if (!aOutRange.IsValid())
859  aOutRange = rDPObj.GetOutRange();
860 
861  if (aOutRange.IsValid() && aResRange.IsValid())
862  {
863  nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row();
864  nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col();
865  }
866 
867  pPivotStrm->write("<")->writeId(XML_location);
868  rStrm.WriteAttributes(XML_ref,
869  XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aOutRange),
870  XML_firstHeaderRow, OUString::number(nFirstHeaderRow),
871  XML_firstDataRow, OUString::number(nFirstDataRow),
872  XML_firstDataCol, OUString::number(nFirstDataCol));
873 
874  if (!aPageFields.empty())
875  {
876  rStrm.WriteAttributes(XML_rowPageCount, OUString::number(static_cast<long>(aPageFields.size())));
877  rStrm.WriteAttributes(XML_colPageCount, OUString::number(1));
878  }
879 
880  pPivotStrm->write("/>");
881 
882  // <pivotFields> - It must contain all fields in the pivot cache even if
883  // only some of them are used in the pivot table. The order must be as
884  // they appear in the cache.
885 
886  pPivotStrm->startElement(XML_pivotFields,
887  XML_count, OString::number(static_cast<long>(aCachedDims.size())));
888 
889  for (size_t i = 0; i < nFieldCount; ++i)
890  {
891  const ScDPSaveDimension* pDim = aCachedDims[i];
892  if (!pDim)
893  {
894  pPivotStrm->singleElement(XML_pivotField,
895  XML_compact, ToPsz10(false),
896  XML_showAll, ToPsz10(false));
897  continue;
898  }
899 
900  bool bDimInTabularMode = false;
901  if(pDim->GetLayoutInfo())
902  bDimInTabularMode = (pDim->GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
903 
904  sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
905 
906  if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN)
907  {
908  if(bDimInTabularMode)
909  {
910  pPivotStrm->singleElement(XML_pivotField,
911  XML_compact, ToPsz10(false),
912  XML_showAll, ToPsz10(false),
913  XML_outline, ToPsz10(false));
914  }
915  else
916  {
917  pPivotStrm->singleElement(XML_pivotField,
918  XML_compact, ToPsz10(false),
919  XML_showAll, ToPsz10(false));
920  }
921  continue;
922  }
923 
924  if (eOrient == sheet::DataPilotFieldOrientation_DATA)
925  {
926  if(bDimInTabularMode)
927  {
928  pPivotStrm->singleElement(XML_pivotField,
929  XML_dataField, ToPsz10(true),
930  XML_compact, ToPsz10(false),
931  XML_showAll, ToPsz10(false),
932  XML_outline, ToPsz10(false));
933  }
934  else
935  {
936  pPivotStrm->singleElement(XML_pivotField,
937  XML_dataField, ToPsz10(true),
938  XML_compact, ToPsz10(false),
939  XML_showAll, ToPsz10(false));
940  }
941  continue;
942  }
943 
944  // Dump field items.
945  std::vector<ScDPLabelData::Member> aMembers;
946  {
947  // We need to get the members in actual order, getting which requires non-const reference here
948  auto& dpo = const_cast<ScDPObject&>(rDPObj);
949  dpo.GetMembers(i, dpo.GetUsedHierarchy(i), aMembers);
950  }
951 
952  std::vector<OUString> aCacheFieldItems;
953  if (i < rCache.GetFieldCount() && !rCache.GetGroupType(i))
954  {
955  for (const auto& it : rCache.GetDimMemberValues(i))
956  {
957  OUString sFormattedName;
958  if (it.HasStringData() || it.IsEmpty())
959  sFormattedName = it.GetString();
960  else
961  sFormattedName = const_cast<ScDPObject&>(rDPObj).GetFormattedString(
962  pDim->GetName(), it.GetValue());
963  aCacheFieldItems.push_back(sFormattedName);
964  }
965  }
966  else
967  {
968  aCacheFieldItems = SortGroupItems(rCache, i);
969  }
970  // The pair contains the member index in cache and if it is hidden
971  std::vector< std::pair<size_t, bool> > aMemberSequence;
972  std::set<size_t> aUsedCachePositions;
973  for (const auto & rMember : aMembers)
974  {
975  auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
976  if (it != aCacheFieldItems.end())
977  {
978  size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
979  auto aInserted = aUsedCachePositions.insert(nCachePos);
980  if (aInserted.second)
981  aMemberSequence.emplace_back(std::make_pair(nCachePos, !rMember.mbVisible));
982  }
983  }
984  // Now add all remaining cache items as hidden
985  for (size_t nItem = 0; nItem < aCacheFieldItems.size(); ++nItem)
986  {
987  if (aUsedCachePositions.find(nItem) == aUsedCachePositions.end())
988  aMemberSequence.emplace_back(nItem, true);
989  }
990 
991  // tdf#125086: check if this field *also* appears in Data region
992  bool bAppearsInData = false;
993  {
994  OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
995  const auto it = std::find_if(
996  aDataFields.begin(), aDataFields.end(), [&aSrcName](const DataField& rDataField) {
997  OUString aThisName
998  = ScDPUtil::getSourceDimensionName(rDataField.mpDim->GetName());
999  return aThisName == aSrcName;
1000  });
1001  if (it != aDataFields.end())
1002  bAppearsInData = true;
1003  }
1004 
1006  pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
1007  if (bAppearsInData)
1008  pAttList->add(XML_dataField, ToPsz10(true));
1009  pAttList->add(XML_compact, ToPsz10(false));
1010  pAttList->add(XML_showAll, ToPsz10(false));
1011 
1012  long nSubTotalCount = pDim->GetSubTotalsCount();
1013  std::vector<OString> aSubtotalSequence;
1014  bool bHasDefaultSubtotal = false;
1015  for (long nSubTotal = 0; nSubTotal < nSubTotalCount; ++nSubTotal)
1016  {
1017  ScGeneralFunction eFunc = pDim->GetSubTotalFunc(nSubTotal);
1018  aSubtotalSequence.push_back(GetSubtotalFuncName(eFunc));
1019  sal_Int32 nAttToken = GetSubtotalAttrToken(eFunc);
1020  if (nAttToken == XML_defaultSubtotal)
1021  bHasDefaultSubtotal = true;
1022  else if (!pAttList->hasAttribute(nAttToken))
1023  pAttList->add(nAttToken, ToPsz10(true));
1024  }
1025  // XML_defaultSubtotal is true by default; only write it if it's false
1026  if (!bHasDefaultSubtotal)
1027  pAttList->add(XML_defaultSubtotal, ToPsz10(false));
1028 
1029  if(bDimInTabularMode)
1030  pAttList->add( XML_outline, ToPsz10(false));
1031  sax_fastparser::XFastAttributeListRef xAttributeList(pAttList);
1032  pPivotStrm->startElement(XML_pivotField, xAttributeList);
1033 
1034  pPivotStrm->startElement(XML_items,
1035  XML_count, OString::number(static_cast<long>(aMemberSequence.size() + aSubtotalSequence.size())));
1036 
1037  for (const auto & nMember : aMemberSequence)
1038  {
1040  if (nMember.second)
1041  pItemAttList->add(XML_h, ToPsz10(true));
1042  pItemAttList->add(XML_x, OString::number(static_cast<long>(nMember.first)));
1043  sax_fastparser::XFastAttributeListRef xItemAttributeList(pItemAttList);
1044  pPivotStrm->singleElement(XML_item, xItemAttributeList);
1045  }
1046 
1047  for (const OString& sSubtotal : aSubtotalSequence)
1048  {
1049  pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
1050  }
1051 
1052  pPivotStrm->endElement(XML_items);
1053  pPivotStrm->endElement(XML_pivotField);
1054  }
1055 
1056  pPivotStrm->endElement(XML_pivotFields);
1057 
1058  // <rowFields>
1059 
1060  if (!aRowFields.empty())
1061  {
1062  pPivotStrm->startElement(XML_rowFields,
1063  XML_count, OString::number(static_cast<long>(aRowFields.size())));
1064 
1065  for (const auto& rRowField : aRowFields)
1066  {
1067  pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
1068  }
1069 
1070  pPivotStrm->endElement(XML_rowFields);
1071  }
1072 
1073  // <rowItems>
1074 
1075  // <colFields>
1076 
1077  if (!aColFields.empty())
1078  {
1079  pPivotStrm->startElement(XML_colFields,
1080  XML_count, OString::number(static_cast<long>(aColFields.size())));
1081 
1082  for (const auto& rColField : aColFields)
1083  {
1084  pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
1085  }
1086 
1087  pPivotStrm->endElement(XML_colFields);
1088  }
1089 
1090  // <colItems>
1091 
1092  // <pageFields>
1093 
1094  if (!aPageFields.empty())
1095  {
1096  pPivotStrm->startElement(XML_pageFields,
1097  XML_count, OString::number(static_cast<long>(aPageFields.size())));
1098 
1099  for (const auto& rPageField : aPageFields)
1100  {
1101  pPivotStrm->singleElement(XML_pageField,
1102  XML_fld, OString::number(rPageField),
1103  XML_hier, OString::number(-1)); // TODO : handle this correctly.
1104  }
1105 
1106  pPivotStrm->endElement(XML_pageFields);
1107  }
1108 
1109  // <dataFields>
1110 
1111  if (!aDataFields.empty())
1112  {
1113  css::uno::Reference<css::container::XNameAccess> xDimsByName;
1114  if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
1115  xDimsByName = xDimSupplier->getDimensions();
1116 
1117  pPivotStrm->startElement(XML_dataFields,
1118  XML_count, OString::number(static_cast<long>(aDataFields.size())));
1119 
1120  for (const auto& rDataField : aDataFields)
1121  {
1122  long nDimIdx = rDataField.mnPos;
1123  assert(aCachedDims[nDimIdx]); // the loop above should have screened for NULL's.
1124  const ScDPSaveDimension& rDim = *rDataField.mpDim;
1125  std::optional<OUString> pName = rDim.GetLayoutName();
1126  // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
1127  // Excel (at least 2016) seems to insist on the presence of "name" attribute in
1128  // dataField element.
1129  // tdf#124881: try to create a meaningful name; don't use empty string.
1130  if (!pName)
1132  rDim.GetName(), ScDPUtil::toSubTotalFunc(rDim.GetFunction()));
1134  pItemAttList->add(XML_name, pName->toUtf8());
1135  pItemAttList->add(XML_fld, OString::number(nDimIdx));
1136  const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
1137  if (pSubtotal)
1138  pItemAttList->add(XML_subtotal, pSubtotal);
1139  if (xDimsByName)
1140  {
1141  try
1142  {
1143  css::uno::Reference<css::beans::XPropertySet> xDimProps(
1144  xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
1145  css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
1146  sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
1147  sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
1148  pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
1149  }
1150  catch (uno::Exception&)
1151  {
1152  SAL_WARN("sc.filter",
1153  "Couldn't get number format for data field " << rDim.GetName());
1154  // Just skip exporting number format
1155  }
1156  }
1157  sax_fastparser::XFastAttributeListRef xItemAttributeList(pItemAttList);
1158  pPivotStrm->singleElement(XML_dataField, xItemAttributeList);
1159  }
1160 
1161  pPivotStrm->endElement(XML_dataFields);
1162  }
1163 
1164  // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
1165  if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
1166  WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
1167  else
1168  pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
1169  XML_showRowHeaders, "1", XML_showColHeaders, "1",
1170  XML_showRowStripes, "0", XML_showColStripes, "0",
1171  XML_showLastColumn, "1");
1172 
1173  OUString aBuf = "../pivotCache/pivotCacheDefinition" +
1174  OUString::number(nCacheId) +
1175  ".xml";
1176 
1177  rStrm.addRelation(
1178  pPivotStrm->getOutputStream(),
1179  CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
1180  aBuf);
1181 
1182  pPivotStrm->endElement(XML_pivotTableDefinition);
1183 }
1184 
1185 void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
1186 {
1187  maTables.emplace_back(pTable, nCacheId, nPivotId);
1188 }
1189 
1190 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void WriteAttributes(sal_Int32 nAttribute, Str &&value, Args &&...rest)
Definition: xestream.hxx:299
OUString GetDimName(long nDim, bool &rIsDataLayout, sal_Int32 *pFlags=nullptr)
Definition: dpobject.cxx:1196
SC_DLLPUBLIC long GetDataDimensionCount() const
Definition: dpsave.cxx:943
const std::optional< OUString > & GetLayoutName() const
Definition: dpsave.cxx:384
size_t GetGroupFieldCount() const
Definition: dpcache.cxx:1029
static OString ToOString(const Color &rColor)
Definition: xestream.cxx:698
ScAddress aStart
Definition: address.hxx:500
const XclExpRoot & GetRoot() const
Returns the filter root data.
Definition: xestream.hxx:290
SC_DLLPUBLIC size_t GetCount() const
Definition: dpobject.cxx:3677
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:919
SCROW Row() const
Definition: address.hxx:262
SC_DLLPUBLIC SheetCaches & GetSheetCaches()
Definition: dpobject.cxx:3754
static FastAttributeList * createAttrList()
bool HasCaches() const
Definition: xepivotxml.cxx:167
size_t GetFieldCount() const
Definition: dpcache.cxx:1024
std::unique_ptr< ContentProperties > pData
sal_Int32 GetGroupType(long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1395
css::sheet::DataPilotFieldOrientation GetOrientation() const
Definition: dpsave.hxx:202
CacheIdMapType maCacheIdMap
Definition: xepivotxml.hxx:87
css::uno::Reference< css::xml::sax::XFastAttributeList > XFastAttributeListRef
XclExpXmlPivotTableManager(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:545
const css::sheet::DataPilotFieldLayoutInfo * GetLayoutInfo() const
Definition: dpsave.hxx:194
SC_DLLPUBLIC ScDPDimensionSaveData * GetDimensionData()
Definition: dpsave.cxx:1196
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:346
const OUString & GetSourceDimName() const
Definition: dpdimsave.hxx:105
aBuf
long GetSubTotalsCount() const
Definition: dpsave.hxx:149
numerical values are counted.
const ScDPItemDataVec & GetDimMemberValues(SCCOL nDim) const
Definition: dpcache.cxx:1053
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:278
SvNumberFormatter & GetFormatter() const
Returns the number formatter of the Calc document.
Definition: xlroot.cxx:315
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:47
product of all numerical values is calculated.
Access to global data from other classes.
Definition: xeroot.hxx:113
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:57
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
int nCount
void PushStream(sax_fastparser::FSHelperPtr const &aStream)
Definition: xestream.cxx:916
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:271
std::vector< Entry > maCaches
Definition: xepivotxml.hxx:43
variance is calculated based on the entire population.
ScGeneralFunction GetFunction() const
Definition: dpsave.hxx:165
sax_fastparser::FSHelperPtr & GetCurrentStream()
Definition: xestream.cxx:910
void PopStream()
Definition: xestream.cxx:921
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:147
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:58
Type GetType() const
Definition: dpitemdata.hxx:68
bool IsDateDimension(long nDim) const
Definition: dpcache.cxx:1069
OUString GetFormattedString(long nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dpcache.cxx:1211
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
SC_DLLPUBLIC bool HasPivotTable() const
Definition: documen3.cxx:341
int i
SC_DLLPUBLIC const std::vector< ScRange > & getAllRanges() const
Definition: dpobject.cxx:3087
uno_Any a
Stores and manages all caches from internal sheets.
Definition: dpobject.hxx:288
average of all numerical values is calculated.
void GetGroupDimMemberIds(long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1323
sal_uInt16 Insert(sal_uInt32 nScNumFmt)
Inserts a number format into the format buffer.
Definition: xestyle.cxx:1354
sal_Int16 SCCOL
Definition: types.hxx:22
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:1039
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)
SC_DLLPUBLIC ScDPSaveDimension * GetExistingDimensionByName(const OUString &rName) const
Definition: dpsave.cxx:846
Entry(const ScDPObject *pTable, sal_Int32 nCacheId, sal_Int32 nPivotId)
used as [n] in pivotTable[n].xml part name.
Definition: xepivotxml.cxx:632
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
const ScDPCache * mpCache
Definition: xepivotxml.hxx:28
XclExpXmlPivotCaches(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:128
std::shared_ptr< FastSerializerHelper > FSHelperPtr
bool IsValid() const
Definition: address.hxx:547
XclExpXmlPivotCaches & GetCaches()
Definition: xepivotxml.cxx:621
SCCOL Col() const
Definition: address.hxx:267
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:119
constexpr T & temporary(T &&x)
static OUString GetStreamName(const char *sStreamDir, const char *sStream, sal_Int32 nId)
Definition: xestream.cxx:683
XclExpXmlPivotTables(const XclExpRoot &rRoot, const XclExpXmlPivotCaches &rCaches)
Definition: xepivotxml.cxx:635
const IndexArrayType * GetFieldIndexArray(size_t nDim) const
Definition: dpcache.cxx:1045
OUString addRelation(const OUString &rType, const OUString &rTarget)
sal_Int32 SCROW
Definition: types.hxx:18
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:638
#define SC_UNONAME_NUMFMT
Definition: unonames.hxx:106
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
const ScDPObjectSet & GetAllReferences() const
Definition: dpcache.cxx:1120
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:87
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:746
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:390
#define SAL_WARN(area, stream)
const ScDPNumGroupInfo * GetNumGroupInfo(long nDim) const
Definition: dpcache.cxx:1374
if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
const ScDPItemData * GetItemDataById(long nDim, SCROW nId) const
Definition: dpcache.cxx:986
::std::vector< sal_Int32 > ScfInt32Vec
Definition: ftools.hxx:254
SCCOL GetDimensionIndex(const OUString &sName) const
Definition: dpcache.cxx:1088
const Date & GetNullDate() const
XclExpXmlPivotTables * GetTablesBySheet(SCTAB nTab)
Definition: xepivotxml.cxx:626
virtual void SaveXml(XclExpXmlStream &rStrm) override
Definition: xepivotxml.cxx:131
sax_fastparser::FSHelperPtr CreateOutputStream(const OUString &sFullStream, const OUString &sRelativeStream, const css::uno::Reference< css::io::XOutputStream > &xParentRelation, const char *sContentType, const char *sRelationshipType, OUString *pRelationshipId=nullptr)
Definition: xestream.cxx:939
SC_DLLPUBLIC ScDPCache * getExistingCache(const ScRange &rRange)
Definition: dpobject.cxx:2968
ScRange GetOutputRangeByType(sal_Int32 nType)
Definition: dpobject.cxx:911
std::set< ScDPObject * > ScDPObjectSet
Definition: dpcache.hxx:53
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:405
sal_Int16 SCTAB
Definition: types.hxx:23
ScGeneralFunction GetSubTotalFunc(long nIndex) const
Definition: dpsave.hxx:152
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo