LibreOffice Module sc (master)  1
xepivotxml.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <xepivotxml.hxx>
11 #include <dpcache.hxx>
12 #include <dpdimsave.hxx>
13 #include <dpitemdata.hxx>
14 #include <dpobject.hxx>
15 #include <dpsave.hxx>
16 #include <dputil.hxx>
17 #include <document.hxx>
18 #include <generalfunction.hxx>
19 #include <unonames.hxx>
20 #include <xestyle.hxx>
21 #include <xeroot.hxx>
22 
23 #include <o3tl/temporary.hxx>
24 #include <o3tl/safeint.hxx>
25 #include <oox/export/utils.hxx>
26 #include <oox/token/namespaces.hxx>
27 #include <sal/log.hxx>
28 #include <sax/tools/converter.hxx>
29 #include <sax/fastattribs.hxx>
30 #include <svl/numformat.hxx>
31 
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
34 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
35 #include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
36 #include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
37 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
38 
39 #include <vector>
40 
41 using namespace oox;
42 using namespace com::sun::star;
43 
44 namespace {
45 
46 void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache )
47 {
48  SCROW nCount = rCache.GetDataSize();
49  size_t nFieldCount = rCache.GetFieldCount();
50 
52  pRecStrm->startElement(XML_pivotCacheRecords,
53  XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
54  FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
55  XML_count, OString::number(static_cast<tools::Long>(nCount)));
56 
57  for (SCROW i = 0; i < nCount; ++i)
58  {
59  pRecStrm->startElement(XML_r);
60  for (size_t nField = 0; nField < nFieldCount; ++nField)
61  {
62  const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField);
63  assert(pArray);
64  assert(o3tl::make_unsigned(i) < pArray->size());
65 
66  // We are using XML_x reference (like: <x v="0"/>), instead of values here (eg: <s v="No Discount"/>).
67  // That's why in SavePivotCacheXml method, we need to list all items.
68  pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]));
69  }
70  pRecStrm->endElement(XML_r);
71  }
72 
73  pRecStrm->endElement(XML_pivotCacheRecords);
74 }
75 
76 const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient )
77 {
78  switch (eOrient)
79  {
80  case sheet::DataPilotFieldOrientation_COLUMN:
81  return "axisCol";
82  case sheet::DataPilotFieldOrientation_ROW:
83  return "axisRow";
84  case sheet::DataPilotFieldOrientation_PAGE:
85  return "axisPage";
86  case sheet::DataPilotFieldOrientation_DATA:
87  return "axisValues";
88  case sheet::DataPilotFieldOrientation_HIDDEN:
89  default:
90  ;
91  }
92 
93  return "";
94 }
95 
96 const char* toOOXMLSubtotalType(ScGeneralFunction eFunc)
97 {
98  switch (eFunc)
99  {
101  return "sum";
103  return "count";
105  return "average";
107  return "max";
109  return "min";
111  return "product";
113  return "countNums";
115  return "stdDev";
117  return "stdDevp";
119  return "var";
121  return "varp";
122  default:
123  ;
124  }
125  return nullptr;
126 }
127 
128 }
129 
131  XclExpRoot(rRoot) {}
132 
134 {
135  sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream();
136  pWorkbookStrm->startElement(XML_pivotCaches);
137 
138  for (size_t i = 0, n = maCaches.size(); i < n; ++i)
139  {
140  const Entry& rEntry = maCaches[i];
141 
142  sal_Int32 nCacheId = i + 1;
143  OUString aRelId;
145  XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId),
146  XclXmlUtils::GetStreamName(nullptr, "pivotCache/pivotCacheDefinition", nCacheId),
147  rStrm.GetCurrentStream()->getOutputStream(),
148  CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"),
149  CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
150  &aRelId);
151 
152  pWorkbookStrm->singleElement(XML_pivotCache,
153  XML_cacheId, OString::number(nCacheId),
154  FSNS(XML_r, XML_id), aRelId.toUtf8());
155 
156  rStrm.PushStream(pPCStrm);
157  SavePivotCacheXml(rStrm, rEntry, nCacheId);
158  rStrm.PopStream();
159  }
160 
161  pWorkbookStrm->endElement(XML_pivotCaches);
162 }
163 
164 void XclExpXmlPivotCaches::SetCaches( std::vector<Entry>&& rCaches )
165 {
166  maCaches = std::move(rCaches);
167 }
168 
170 {
171  return !maCaches.empty();
172 }
173 
175 {
176  if (nCacheId <= 0)
177  // cache ID is 1-based.
178  return nullptr;
179 
180  size_t nPos = nCacheId - 1;
181  if (nPos >= maCaches.size())
182  return nullptr;
183 
184  return &maCaches[nPos];
185 }
186 
187 namespace {
196 OUString GetExcelFormattedDate( double fSerialDateTime, const SvNumberFormatter& rFormatter )
197 {
198  // tdf#125055: properly round the value to seconds when truncating nanoseconds below
199  constexpr double fHalfSecond = 1 / 86400.0 * 0.5;
200  css::util::DateTime aUDateTime
201  = (DateTime(rFormatter.GetNullDate()) + fSerialDateTime + fHalfSecond).GetUNODateTime();
202  // We need to reset nanoseconds, to avoid string like: "1982-02-18T16:04:47.999999849"
203  aUDateTime.NanoSeconds = 0;
204  OUStringBuffer sBuf;
205  ::sax::Converter::convertDateTime(sBuf, aUDateTime, nullptr, true);
206  return sBuf.makeStringAndClear();
207 }
208 
209 // Excel seems to expect different order of group item values; we need to rearrange elements
210 // to output "<date1" first, then elements, then ">date2" last.
211 // Since ScDPItemData::DateFirst is -1, ScDPItemData::DateLast is 10000, and other date group
212 // items would fit between those in order (like 0 = Jan, 1 = Feb, etc.), we can simply sort
213 // the items by value.
214 std::vector<OUString> SortGroupItems(const ScDPCache& rCache, tools::Long nDim)
215 {
216  struct ItemData
217  {
218  sal_Int32 nVal;
219  const ScDPItemData* pData;
220  };
221  std::vector<ItemData> aDataToSort;
222  ScfInt32Vec aGIIds;
223  rCache.GetGroupDimMemberIds(nDim, aGIIds);
224  for (sal_Int32 id : aGIIds)
225  {
226  const ScDPItemData* pGIData = rCache.GetItemDataById(nDim, id);
227  if (pGIData->GetType() == ScDPItemData::GroupValue)
228  {
229  auto aGroupVal = pGIData->GetGroupValue();
230  aDataToSort.push_back({ aGroupVal.mnValue, pGIData });
231  }
232  }
233  std::sort(aDataToSort.begin(), aDataToSort.end(),
234  [](const ItemData& a, const ItemData& b) { return a.nVal < b.nVal; });
235 
236  std::vector<OUString> aSortedResult;
237  for (const auto& el : aDataToSort)
238  {
239  aSortedResult.push_back(rCache.GetFormattedString(nDim, *el.pData, false));
240  }
241  return aSortedResult;
242 }
243 } // namespace
244 
245 void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter )
246 {
247  assert(rEntry.mpCache);
248  const ScDPCache& rCache = *rEntry.mpCache;
249 
250  sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream();
251 
252  OUString aRelId;
254  XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter),
255  XclXmlUtils::GetStreamName(nullptr, "pivotCacheRecords", nCounter),
256  pDefStrm->getOutputStream(),
257  CREATE_XL_CONTENT_TYPE("pivotCacheRecords"),
258  CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"),
259  &aRelId);
260 
261  rStrm.PushStream(pRecStrm);
262  savePivotCacheRecordsXml(rStrm, rCache);
263  rStrm.PopStream();
264 
265  pDefStrm->startElement(XML_pivotCacheDefinition,
266  XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
267  FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
268  FSNS(XML_r, XML_id), aRelId.toUtf8(),
269  XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()),
270  XML_createdVersion, "3"); // MS Excel 2007, tdf#112936: setting version number makes MSO to handle the pivot table differently
271 
272  pDefStrm->startElement(XML_cacheSource, XML_type, "worksheet");
273 
274  OUString aSheetName;
275  GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName);
276  pDefStrm->singleElement(XML_worksheetSource,
277  XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rEntry.maSrcRange),
278  XML_sheet, aSheetName.toUtf8());
279 
280  pDefStrm->endElement(XML_cacheSource);
281 
282  size_t nCount = rCache.GetFieldCount();
283  const size_t nGroupFieldCount = rCache.GetGroupFieldCount();
284  pDefStrm->startElement(XML_cacheFields,
285  XML_count, OString::number(static_cast<tools::Long>(nCount + nGroupFieldCount)));
286 
287  auto WriteFieldGroup = [this, &rCache, pDefStrm](size_t i, size_t base) {
288  const sal_Int32 nDatePart = rCache.GetGroupType(i);
289  if (!nDatePart)
290  return;
291  OString sGroupBy;
292  switch (nDatePart)
293  {
294  case sheet::DataPilotFieldGroupBy::SECONDS:
295  sGroupBy = "seconds";
296  break;
297  case sheet::DataPilotFieldGroupBy::MINUTES:
298  sGroupBy = "minutes";
299  break;
300  case sheet::DataPilotFieldGroupBy::HOURS:
301  sGroupBy = "hours";
302  break;
303  case sheet::DataPilotFieldGroupBy::DAYS:
304  sGroupBy = "days";
305  break;
306  case sheet::DataPilotFieldGroupBy::MONTHS:
307  sGroupBy = "months";
308  break;
309  case sheet::DataPilotFieldGroupBy::QUARTERS:
310  sGroupBy = "quarters";
311  break;
312  case sheet::DataPilotFieldGroupBy::YEARS:
313  sGroupBy = "years";
314  break;
315  }
316 
317  // fieldGroup element
318  pDefStrm->startElement(XML_fieldGroup, XML_base, OString::number(base));
319 
320  SvNumberFormatter& rFormatter = GetFormatter();
321 
322  // rangePr element
323  const ScDPNumGroupInfo* pGI = rCache.GetNumGroupInfo(i);
325  pGroupAttList->add(XML_groupBy, sGroupBy);
326  // Possible TODO: find out when to write autoStart attribute for years grouping
327  pGroupAttList->add(XML_startDate, GetExcelFormattedDate(pGI->mfStart, rFormatter).toUtf8());
328  pGroupAttList->add(XML_endDate, GetExcelFormattedDate(pGI->mfEnd, rFormatter).toUtf8());
329  if (pGI->mfStep)
330  pGroupAttList->add(XML_groupInterval, OString::number(pGI->mfStep));
331  pDefStrm->singleElement(XML_rangePr, pGroupAttList);
332 
333  // groupItems element
334  auto aElemVec = SortGroupItems(rCache, i);
335  pDefStrm->startElement(XML_groupItems, XML_count, OString::number(aElemVec.size()));
336  for (const auto& sElem : aElemVec)
337  {
338  pDefStrm->singleElement(XML_s, XML_v, sElem.toUtf8());
339  }
340  pDefStrm->endElement(XML_groupItems);
341  pDefStrm->endElement(XML_fieldGroup);
342  };
343 
344  for (size_t i = 0; i < nCount; ++i)
345  {
346  OUString aName = rCache.GetDimensionName(i);
347 
348  pDefStrm->startElement(XML_cacheField,
349  XML_name, aName.toUtf8(),
350  XML_numFmtId, OString::number(0));
351 
352  const ScDPCache::ScDPItemDataVec& rFieldItems = rCache.GetDimMemberValues(i);
353 
354  std::set<ScDPItemData::Type> aDPTypes;
355  double fMin = std::numeric_limits<double>::infinity(), fMax = -std::numeric_limits<double>::infinity();
356  bool isValueInteger = true;
357  bool isContainsDate = rCache.IsDateDimension(i);
358  bool isLongText = false;
359  for (const auto& rFieldItem : rFieldItems)
360  {
361  ScDPItemData::Type eType = rFieldItem.GetType();
362  // tdf#123939 : error and string are same for cache; if both are present, keep only one
363  if (eType == ScDPItemData::Error)
364  eType = ScDPItemData::String;
365  aDPTypes.insert(eType);
366  if (eType == ScDPItemData::Value)
367  {
368  double fVal = rFieldItem.GetValue();
369  fMin = std::min(fMin, fVal);
370  fMax = std::max(fMax, fVal);
371 
372  // Check if all values are integers
373  if (isValueInteger && (modf(fVal, &o3tl::temporary(double())) != 0.0))
374  {
375  isValueInteger = false;
376  }
377  }
378  else if (eType == ScDPItemData::String && !isLongText)
379  {
380  isLongText = rFieldItem.GetString().getLength() > 255;
381  }
382  }
383 
385  // TODO In same cases, disable listing of items, as it is done in MS Excel.
386  // Exporting savePivotCacheRecordsXml method needs to be updated accordingly
387  //bool bListItems = true;
388 
389  std::set<ScDPItemData::Type> aDPTypesWithoutBlank = aDPTypes;
390  aDPTypesWithoutBlank.erase(ScDPItemData::Empty);
391 
392  const bool isContainsString = aDPTypesWithoutBlank.count(ScDPItemData::String) > 0;
393  const bool isContainsBlank = aDPTypes.count(ScDPItemData::Empty) > 0;
394  const bool isContainsNumber
395  = !isContainsDate && aDPTypesWithoutBlank.count(ScDPItemData::Value) > 0;
396  bool isContainsNonDate = !(isContainsDate && aDPTypesWithoutBlank.size() <= 1);
397 
398  // XML_containsSemiMixedTypes possible values:
399  // 1 - (Default) at least one text value, or can also contain a mix of other data types and blank values,
400  // or blank values only
401  // 0 - the field does not have a mix of text and other values
402  if (!(isContainsString || (aDPTypes.size() > 1) || (isContainsBlank && aDPTypesWithoutBlank.empty())))
403  pAttList->add(XML_containsSemiMixedTypes, ToPsz10(false));
404 
405  if (!isContainsNonDate)
406  pAttList->add(XML_containsNonDate, ToPsz10(false));
407 
408  if (isContainsDate)
409  pAttList->add(XML_containsDate, ToPsz10(true));
410 
411  // default for containsString field is true, so we are writing only when is false
412  if (!isContainsString)
413  pAttList->add(XML_containsString, ToPsz10(false));
414 
415  if (isContainsBlank)
416  pAttList->add(XML_containsBlank, ToPsz10(true));
417 
418  // XML_containsMixedType possible values:
419  // 1 - field contains more than one data type
420  // 0 - (Default) only one data type. The field can still contain blank values (that's why we are using aDPTypesWithoutBlank)
421  if (aDPTypesWithoutBlank.size() > 1)
422  pAttList->add(XML_containsMixedTypes, ToPsz10(true));
423 
424  // If field contain mixed types (Date and Numbers), MS Excel is saving only "minDate" and "maxDate" and not "minValue" and "maxValue"
425  // Example how Excel is saving mixed Date and Numbers:
426  // <sharedItems containsSemiMixedTypes="0" containsDate="1" containsString="0" containsMixedTypes="1" minDate="1900-01-03T22:26:04" maxDate="1900-01-07T14:02:04" />
427  // Example how Excel is saving Dates only:
428  // <sharedItems containsSemiMixedTypes="0" containsNonDate="0" containsDate="1" containsString="0" minDate="1903-08-24T07:40:48" maxDate="2024-05-23T07:12:00"/>
429  if (isContainsNumber)
430  pAttList->add(XML_containsNumber, ToPsz10(true));
431 
432  if (isValueInteger && isContainsNumber)
433  pAttList->add(XML_containsInteger, ToPsz10(true));
434 
435 
436  // Number type fields could be mixed with blank types, and it shouldn't be treated as listed items.
437  // Example:
438  // <cacheField name="employeeID" numFmtId="0">
439  // <sharedItems containsString="0" containsBlank="1" containsNumber="1" containsInteger="1" minValue="35" maxValue="89"/>
440  // </cacheField>
441  if (isContainsNumber)
442  {
443  pAttList->add(XML_minValue, OString::number(fMin));
444  pAttList->add(XML_maxValue, OString::number(fMax));
445  }
446 
447  if (isContainsDate)
448  {
449  pAttList->add(XML_minDate, GetExcelFormattedDate(fMin, GetFormatter()).toUtf8());
450  pAttList->add(XML_maxDate, GetExcelFormattedDate(fMax, GetFormatter()).toUtf8());
451  }
452 
453  //if (bListItems) // see TODO above
454  {
455  pAttList->add(XML_count, OString::number(static_cast<tools::Long>(rFieldItems.size())));
456  }
457 
458  if (isLongText)
459  {
460  pAttList->add(XML_longText, ToPsz10(true));
461  }
462 
463  pDefStrm->startElement(XML_sharedItems, pAttList);
464 
465  //if (bListItems) // see TODO above
466  {
467  for (const ScDPItemData& rItem : rFieldItems)
468  {
469  switch (rItem.GetType())
470  {
472  pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
473  break;
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(std::move(aCaches));
619 }
620 
622 {
623  return maCaches;
624 }
625 
627 {
628  TablesType::iterator const it = m_Tables.find(nTab);
629  return it == m_Tables.end() ? nullptr : it->second.get();
630 }
631 
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  tools::Long mnPos; // field index in pivot cache.
666  const ScDPSaveDimension* mpDim;
667 
668  DataField( tools::Long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
669 };
670 
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<tools::Long> aRowFields;
773  std::vector<tools::Long> aColFields;
774  std::vector<tools::Long> aPageFields;
775  std::vector<DataField> aDataFields;
776 
777  tools::Long nDataDimCount = rSaveData.GetDataDimensionCount();
778  // Use dimensions in the save data to get their correct ordering.
779  // Dimension order here is significant as they specify the order of
780  // appearance in each axis.
781  const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
782  bool bTabularMode = false;
783  for (const auto & i : rDims)
784  {
785  const ScDPSaveDimension& rDim = *i;
786 
787  tools::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<tools::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<tools::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  tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
1013  std::vector<OString> aSubtotalSequence;
1014  bool bHasDefaultSubtotal = false;
1015  for (tools::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  pPivotStrm->startElement(XML_pivotField, pAttList);
1032 
1033  pPivotStrm->startElement(XML_items,
1034  XML_count, OString::number(static_cast<tools::Long>(aMemberSequence.size() + aSubtotalSequence.size())));
1035 
1036  for (const auto & nMember : aMemberSequence)
1037  {
1039  if (nMember.second)
1040  pItemAttList->add(XML_h, ToPsz10(true));
1041  pItemAttList->add(XML_x, OString::number(static_cast<tools::Long>(nMember.first)));
1042  pPivotStrm->singleElement(XML_item, pItemAttList);
1043  }
1044 
1045  for (const OString& sSubtotal : aSubtotalSequence)
1046  {
1047  pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
1048  }
1049 
1050  pPivotStrm->endElement(XML_items);
1051  pPivotStrm->endElement(XML_pivotField);
1052  }
1053 
1054  pPivotStrm->endElement(XML_pivotFields);
1055 
1056  // <rowFields>
1057 
1058  if (!aRowFields.empty())
1059  {
1060  pPivotStrm->startElement(XML_rowFields,
1061  XML_count, OString::number(static_cast<tools::Long>(aRowFields.size())));
1062 
1063  for (const auto& rRowField : aRowFields)
1064  {
1065  pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
1066  }
1067 
1068  pPivotStrm->endElement(XML_rowFields);
1069  }
1070 
1071  // <rowItems>
1072 
1073  // <colFields>
1074 
1075  if (!aColFields.empty())
1076  {
1077  pPivotStrm->startElement(XML_colFields,
1078  XML_count, OString::number(static_cast<tools::Long>(aColFields.size())));
1079 
1080  for (const auto& rColField : aColFields)
1081  {
1082  pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
1083  }
1084 
1085  pPivotStrm->endElement(XML_colFields);
1086  }
1087 
1088  // <colItems>
1089 
1090  // <pageFields>
1091 
1092  if (!aPageFields.empty())
1093  {
1094  pPivotStrm->startElement(XML_pageFields,
1095  XML_count, OString::number(static_cast<tools::Long>(aPageFields.size())));
1096 
1097  for (const auto& rPageField : aPageFields)
1098  {
1099  pPivotStrm->singleElement(XML_pageField,
1100  XML_fld, OString::number(rPageField),
1101  XML_hier, OString::number(-1)); // TODO : handle this correctly.
1102  }
1103 
1104  pPivotStrm->endElement(XML_pageFields);
1105  }
1106 
1107  // <dataFields>
1108 
1109  if (!aDataFields.empty())
1110  {
1111  css::uno::Reference<css::container::XNameAccess> xDimsByName;
1112  if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
1113  xDimsByName = xDimSupplier->getDimensions();
1114 
1115  pPivotStrm->startElement(XML_dataFields,
1116  XML_count, OString::number(static_cast<tools::Long>(aDataFields.size())));
1117 
1118  for (const auto& rDataField : aDataFields)
1119  {
1120  tools::Long nDimIdx = rDataField.mnPos;
1121  assert(aCachedDims[nDimIdx]); // the loop above should have screened for NULL's.
1122  const ScDPSaveDimension& rDim = *rDataField.mpDim;
1123  std::optional<OUString> pName = rDim.GetLayoutName();
1124  // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
1125  // Excel (at least 2016) seems to insist on the presence of "name" attribute in
1126  // dataField element.
1127  // tdf#124881: try to create a meaningful name; don't use empty string.
1128  if (!pName)
1130  rDim.GetName(), ScDPUtil::toSubTotalFunc(rDim.GetFunction()));
1132  pItemAttList->add(XML_name, pName->toUtf8());
1133  pItemAttList->add(XML_fld, OString::number(nDimIdx));
1134  const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
1135  if (pSubtotal)
1136  pItemAttList->add(XML_subtotal, pSubtotal);
1137  if (xDimsByName)
1138  {
1139  try
1140  {
1141  css::uno::Reference<css::beans::XPropertySet> xDimProps(
1142  xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
1143  css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
1144  sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
1145  sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
1146  pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
1147  }
1148  catch (uno::Exception&)
1149  {
1150  SAL_WARN("sc.filter",
1151  "Couldn't get number format for data field " << rDim.GetName());
1152  // Just skip exporting number format
1153  }
1154  }
1155  pPivotStrm->singleElement(XML_dataField, pItemAttList);
1156  }
1157 
1158  pPivotStrm->endElement(XML_dataFields);
1159  }
1160 
1161  // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
1162  if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
1163  WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
1164  else
1165  pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
1166  XML_showRowHeaders, "1", XML_showColHeaders, "1",
1167  XML_showRowStripes, "0", XML_showColStripes, "0",
1168  XML_showLastColumn, "1");
1169 
1170  OUString aBuf = "../pivotCache/pivotCacheDefinition" +
1171  OUString::number(nCacheId) +
1172  ".xml";
1173 
1174  rStrm.addRelation(
1175  pPivotStrm->getOutputStream(),
1176  CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
1177  aBuf);
1178 
1179  pPivotStrm->endElement(XML_pivotTableDefinition);
1180 }
1181 
1182 void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
1183 {
1184  maTables.emplace_back(pTable, nCacheId, nPivotId);
1185 }
1186 
1187 /* 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:385
size_t GetGroupFieldCount() const
Definition: dpcache.cxx:1026
static OString ToOString(const Color &rColor)
Definition: xestream.cxx:709
ScAddress aStart
Definition: address.hxx:497
OUString GetFormattedString(tools::Long nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dpcache.cxx:1205
const XclExpRoot & GetRoot() const
Returns the filter root data.
Definition: xestream.hxx:291
SC_DLLPUBLIC size_t GetCount() const
Definition: dpobject.cxx:3679
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:916
SCROW Row() const
Definition: address.hxx:274
SC_DLLPUBLIC SheetCaches & GetSheetCaches()
Definition: dpobject.cxx:3756
bool HasCaches() const
Definition: xepivotxml.cxx:169
size_t GetFieldCount() const
Definition: dpcache.cxx:1021
css::sheet::DataPilotFieldOrientation GetOrientation() const
Definition: dpsave.hxx:202
CacheIdMapType maCacheIdMap
Definition: xepivotxml.hxx:87
XclExpXmlPivotTableManager(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:545
std::unique_ptr< sal_Int32[]> pData
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:1196
#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:1050
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:285
SvNumberFormatter & GetFormatter() const
Returns the number formatter of the Calc document.
Definition: xlroot.cxx:322
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:1085
Access to global data from other classes.
Definition: xeroot.hxx:112
const ScDPSaveGroupDimension * GetNamedGroupDim(const OUString &rGroupDimName) const
Definition: dpdimsave.cxx:635
const ScRange & GetOutRange() const
Definition: dpobject.cxx:409
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:1389
int nCount
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:983
void PushStream(sax_fastparser::FSHelperPtr const &aStream)
Definition: xestream.cxx:918
void SetCaches(std::vector< Entry > &&rCaches)
Definition: xepivotxml.cxx:164
void AppendTable(const ScDPObject *pTable, sal_Int32 nCacheId, sal_Int32 nPivotId)
bool SyncAllDimensionMembers()
Remove in the save data entries for members that don't exist anymore.
Definition: dpobject.cxx:973
SCTAB Tab() const
Definition: address.hxx:283
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:912
const ScDPNumGroupInfo * GetNumGroupInfo(tools::Long nDim) const
Definition: dpcache.cxx:1368
void PopStream()
Definition: xestream.cxx:923
static SC_DLLPUBLIC OUString getSourceDimensionName(std::u16string_view rName)
Definition: dputil.cxx:66
maximum value of all numerical values is calculated.
void SavePivotCacheXml(XclExpXmlStream &rStrm, const Entry &rEntry, sal_Int32 nCacheId)
Definition: xepivotxml.cxx:245
bool GetHeaderLayout() const
Definition: dpobject.hxx:147
DocumentType eType
const Entry * GetCache(sal_Int32 nCacheId) const
Definition: xepivotxml.cxx:174
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:943
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:3089
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:941
XclExpNumFmtBuffer & GetNumFmtBuffer() const
Returns the number format buffer.
Definition: xeroot.cxx:117
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data...
Definition: dpcache.cxx:1036
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: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
XML_type
bool empty() const
SC_DLLPUBLIC ScDPSaveDimension * GetExistingDimensionByName(std::u16string_view rName) const
Definition: dpsave.cxx:846
const ScDPCache * mpCache
Definition: xepivotxml.hxx:27
XclExpXmlPivotCaches(const XclExpRoot &rRoot)
Definition: xepivotxml.cxx:130
std::shared_ptr< FastSerializerHelper > FSHelperPtr
bool IsValid() const
Definition: address.hxx:544
XclExpXmlPivotCaches & GetCaches()
Definition: xepivotxml.cxx:621
SCCOL Col() const
Definition: address.hxx:279
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:694
XclExpXmlPivotTables(const XclExpRoot &rRoot, const XclExpXmlPivotCaches &rCaches)
Definition: xepivotxml.cxx:635
const IndexArrayType * GetFieldIndexArray(size_t nDim) const
Definition: dpcache.cxx:1042
OUString GetDimName(tools::Long nDim, bool &rIsDataLayout, sal_Int32 *pFlags=nullptr)
Definition: dpobject.cxx:1197
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:638
bool IsDateDimension(tools::Long nDim) const
Definition: dpcache.cxx:1066
#define SC_UNONAME_NUMFMT
Definition: unonames.hxx:107
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:1117
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: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:384
#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:1317
XclExpXmlPivotTables * GetTablesBySheet(SCTAB nTab)
Definition: xepivotxml.cxx:626
virtual void SaveXml(XclExpXmlStream &rStrm) override
Definition: xepivotxml.cxx:133
SC_DLLPUBLIC ScDPCache * getExistingCache(const ScRange &rRange)
Definition: dpobject.cxx:2970
ScRange GetOutputRangeByType(sal_Int32 nType)
Definition: dpobject.cxx:910
OUString addRelation(const OUString &rType, std::u16string_view rTarget)
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:215
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:395
sal_Int16 SCTAB
Definition: types.hxx:22
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo