LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sc / source / filter / excel / xepivot.cxx
blob3c0656703be225b0291699b41846503f48a19e24
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <xepivot.hxx>
21 #include <xehelper.hxx>
22 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
23 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
24 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
25 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
26 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
27 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
28 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
30 #include <algorithm>
31 #include <math.h>
32 #include <string_view>
34 #include <osl/diagnose.h>
35 #include <sot/storage.hxx>
36 #include <document.hxx>
37 #include <dpcache.hxx>
38 #include <dpgroup.hxx>
39 #include <dpobject.hxx>
40 #include <dpsave.hxx>
41 #include <dpdimsave.hxx>
42 #include <dpshttab.hxx>
43 #include <globstr.hrc>
44 #include <scresid.hxx>
45 #include <xestring.hxx>
46 #include <xelink.hxx>
47 #include <dputil.hxx>
48 #include <generalfunction.hxx>
49 #include <svl/numformat.hxx>
51 using namespace ::oox;
53 using ::com::sun::star::sheet::DataPilotFieldOrientation;
54 using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
55 using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
56 using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
57 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
58 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
59 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
60 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
61 using ::com::sun::star::sheet::DataPilotFieldReference;
63 // Pivot cache
65 namespace {
67 // constants to track occurrence of specific data types
68 const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
69 const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
70 const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
71 const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
73 /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
74 const sal_uInt16 spnPCItemFlags[] =
75 { // STR DBL INT DAT
76 EXC_SXFIELD_DATA_NONE,
77 EXC_SXFIELD_DATA_STR, // x
78 EXC_SXFIELD_DATA_INT, // x
79 EXC_SXFIELD_DATA_STR_INT, // x x
80 EXC_SXFIELD_DATA_DBL, // x
81 EXC_SXFIELD_DATA_STR_DBL, // x x
82 EXC_SXFIELD_DATA_INT, // x x
83 EXC_SXFIELD_DATA_STR_INT, // x x x
84 EXC_SXFIELD_DATA_DATE, // x
85 EXC_SXFIELD_DATA_DATE_STR, // x x
86 EXC_SXFIELD_DATA_DATE_NUM, // x x
87 EXC_SXFIELD_DATA_DATE_STR, // x x x
88 EXC_SXFIELD_DATA_DATE_NUM, // x x
89 EXC_SXFIELD_DATA_DATE_STR, // x x x
90 EXC_SXFIELD_DATA_DATE_NUM, // x x x
91 EXC_SXFIELD_DATA_DATE_STR // x x x x
94 } // namespace
96 XclExpPCItem::XclExpPCItem( const OUString& rText ) :
97 XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
98 mnTypeFlag( EXC_PCITEM_DATA_STRING )
100 if( !rText.isEmpty() )
101 SetText( rText );
102 else
103 SetEmpty();
106 XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
107 XclExpRecord( EXC_ID_SXDOUBLE, 8 )
109 SetDouble( fValue, rText );
110 mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
111 EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
114 XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
115 XclExpRecord( EXC_ID_SXDATETIME, 8 )
117 SetDateTime( rDateTime, rText );
118 mnTypeFlag = EXC_PCITEM_DATA_DATE;
121 XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
122 XclExpRecord( EXC_ID_SXINTEGER, 2 ),
123 mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
125 SetInteger( nValue );
128 XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
129 XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
130 mnTypeFlag( EXC_PCITEM_DATA_STRING )
132 SetBool( bValue, rText );
135 bool XclExpPCItem::EqualsText( std::u16string_view rText ) const
137 return rText.empty() ? IsEmpty() : (GetText() && (*GetText() == rText));
140 bool XclExpPCItem::EqualsDouble( double fValue ) const
142 return GetDouble() && (*GetDouble() == fValue);
145 bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
147 return GetDateTime() && (*GetDateTime() == rDateTime);
150 bool XclExpPCItem::EqualsBool( bool bValue ) const
152 return GetBool() && (*GetBool() == bValue);
155 void XclExpPCItem::WriteBody( XclExpStream& rStrm )
157 if( const OUString* pText = GetText() )
159 rStrm << XclExpString( *pText );
161 else if( const double* pfValue = GetDouble() )
163 rStrm << *pfValue;
165 else if( const sal_Int16* pnValue = GetInteger() )
167 rStrm << *pnValue;
169 else if( const DateTime* pDateTime = GetDateTime() )
171 sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
172 sal_uInt16 nMonth = pDateTime->GetMonth();
173 sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
174 sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
175 sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
176 sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
177 if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
178 rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
180 else if( const bool* pbValue = GetBool() )
182 rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
184 else
186 // nothing to do for SXEMPTY
187 OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
191 XclExpPCField::XclExpPCField(
192 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
193 const ScDPObject& rDPObj, const ScRange& rRange ) :
194 XclExpRecord( EXC_ID_SXFIELD ),
195 XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
196 XclExpRoot( rRoot ),
197 mnTypeFlags( 0 )
199 // general settings for the standard field, insert all items from source range
200 InitStandardField( rRange );
202 // add special settings for inplace numeric grouping
203 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
205 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
207 if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
209 const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
210 const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
211 OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
212 "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
214 if( rNumInfo.mbEnable )
215 InitNumGroupField( rDPObj, rNumInfo );
216 else if( rDateInfo.mbEnable )
217 InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
222 // final settings (flags, item numbers)
223 Finalize();
226 XclExpPCField::XclExpPCField(
227 const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
228 const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
229 XclExpRecord( EXC_ID_SXFIELD ),
230 XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
231 XclExpRoot( rRoot ),
232 mnTypeFlags( 0 )
234 // add base field info (always using first base field, not predecessor of this field) ***
235 OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
236 "XclExpPCField::FillFromGroup - wrong base cache field" );
237 maFieldInfo.maName = rGroupDim.GetGroupDimName();
238 maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
240 // add standard group info or date group info
241 const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
242 if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
243 InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
244 else
245 InitStdGroupField( rBaseField, rGroupDim );
247 // final settings (flags, item numbers)
248 Finalize();
251 XclExpPCField::~XclExpPCField()
255 void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
257 OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
258 "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
259 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
260 maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
263 sal_uInt16 XclExpPCField::GetItemCount() const
265 return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
268 const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
270 return GetVisItemList().GetRecord( nItemIdx );
273 sal_uInt16 XclExpPCField::GetItemIndex( std::u16string_view rItemName ) const
275 const XclExpPCItemList& rItemList = GetVisItemList();
276 for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
277 if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
278 return static_cast< sal_uInt16 >( nPos );
279 return EXC_PC_NOITEM;
282 std::size_t XclExpPCField::GetIndexSize() const
284 return Has16BitIndexes() ? 2 : 1;
287 void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
289 // only standard fields write item indexes
290 if( nSrcRow < maIndexVec.size() )
292 sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
293 if( Has16BitIndexes() )
294 rStrm << nIndex;
295 else
296 rStrm << static_cast< sal_uInt8 >( nIndex );
300 void XclExpPCField::Save( XclExpStream& rStrm )
302 OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
303 // SXFIELD
304 XclExpRecord::Save( rStrm );
305 // SXFDBTYPE
306 XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
307 // list of grouping items
308 maGroupItemList.Save( rStrm );
309 // SXGROUPINFO
310 WriteSxgroupinfo( rStrm );
311 // SXNUMGROUP and additional grouping items (grouping limit settings)
312 WriteSxnumgroup( rStrm );
313 // list of original items
314 maOrigItemList.Save( rStrm );
317 // private --------------------------------------------------------------------
319 const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
321 OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
322 "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
323 return IsStandardField() ? maOrigItemList : maGroupItemList;
326 void XclExpPCField::InitStandardField( const ScRange& rRange )
328 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
329 OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
331 ScDocument& rDoc = GetDoc();
332 SvNumberFormatter& rFormatter = GetFormatter();
334 // field name is in top cell of the range
335 ScAddress aPos( rRange.aStart );
336 maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
337 // #i76047# maximum field name length in pivot cache is 255
338 if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
339 maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
341 // loop over all cells, create pivot cache items
342 for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
344 OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
345 if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
347 double fValue = rDoc.GetValue( aPos );
348 SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
349 if( nFmtType == SvNumFormatType::LOGICAL )
350 InsertOrigBoolItem( fValue != 0, aText );
351 else if( nFmtType & SvNumFormatType::DATETIME )
352 InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
353 else
354 InsertOrigDoubleItem( fValue, aText );
356 else
358 InsertOrigTextItem( aText );
363 void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
365 OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
367 maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
368 maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
370 // loop over all groups of this field
371 for( tools::Long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
373 const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
374 // the index of the new item containing the grouping name
375 sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
376 // loop over all elements of one group
377 for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
379 if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
381 // try to find the item that is part of the group in the base field
382 sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
383 if( nBaseItemIdx < maFieldInfo.mnBaseItems )
385 // add group name item only if there are any valid base items
386 if( nGroupItemIdx == EXC_PC_NOITEM )
387 nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
388 maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
394 // add items and base item indexes of all ungrouped elements
395 for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
396 // items that are not part of a group still have the EXC_PC_NOITEM entry
397 if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
398 // try to find the base item
399 if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
400 // create a clone of the base item, insert its index into item order list
401 maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
404 void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
406 OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
407 OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
409 // new field type, date type, limit settings (min/max/step/auto)
410 if( rNumInfo.mbDateValues )
412 // special case: group by days with step count
413 meFieldType = EXC_PCFIELD_DATEGROUP;
414 maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
415 SetDateGroupLimit( rNumInfo, true );
417 else
419 meFieldType = EXC_PCFIELD_NUMGROUP;
420 maNumGroupInfo.SetNumType();
421 SetNumGroupLimit( rNumInfo );
424 // generate visible items
425 InsertNumDateGroupItems( rDPObj, rNumInfo );
428 void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
430 OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
431 OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
433 // new field type
434 meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
436 // date type, limit settings (min/max/step/auto)
437 maNumGroupInfo.SetScDateType( nDatePart );
438 SetDateGroupLimit( rDateInfo, false );
440 // generate visible items
441 InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
444 void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
446 OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
447 maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
450 void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
452 size_t nItemIdx = maOrigItemList.GetSize();
453 maOrigItemList.AppendNewRecord( pNewItem );
454 InsertItemArrayIndex( nItemIdx );
455 mnTypeFlags |= pNewItem->GetTypeFlag();
458 void XclExpPCField::InsertOrigTextItem( const OUString& rText )
460 size_t nPos = 0;
461 bool bFound = false;
462 // #i76047# maximum item text length in pivot cache is 255
463 OUString aShortText = rText.copy( 0, ::std::min(rText.getLength(), EXC_PC_MAXSTRLEN ) );
464 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
465 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
466 InsertItemArrayIndex( nPos );
467 if( !bFound )
468 InsertOrigItem( new XclExpPCItem( aShortText ) );
471 void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
473 size_t nPos = 0;
474 bool bFound = false;
475 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
476 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
477 InsertItemArrayIndex( nPos );
478 if( !bFound )
479 InsertOrigItem( new XclExpPCItem( fValue, rText ) );
482 void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
484 size_t nPos = 0;
485 bool bFound = false;
486 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
487 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
488 InsertItemArrayIndex( nPos );
489 if( !bFound )
490 InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
493 void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
495 size_t nPos = 0;
496 bool bFound = false;
497 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
498 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
499 InsertItemArrayIndex( nPos );
500 if( !bFound )
501 InsertOrigItem( new XclExpPCItem( bValue, rText ) );
504 sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
506 maGroupItemList.AppendNewRecord( pNewItem );
507 return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
510 void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
512 OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
513 const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
514 if(!pSrcDesc)
515 return;
517 // get the string collection with original source elements
518 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
519 const ScDPDimensionSaveData* pDimData = nullptr;
520 if (pSaveData)
521 pDimData = pSaveData->GetExistingDimensionData();
523 const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
524 if (!pCache)
525 return;
527 ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
528 tools::Long nDim = GetFieldIndex();
529 // get the string collection with generated grouping elements
530 ScDPNumGroupDimension aTmpDim( rNumInfo );
531 if( nDatePart != 0 )
532 aTmpDim.SetDateDimension();
533 const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
534 static_cast<SCCOL>(nDim), pCache);
535 for (SCROW nMemberId : aMemberIds)
537 const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
538 if ( pData )
540 OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
541 InsertGroupItem(new XclExpPCItem(aStr));
546 void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
548 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
549 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
550 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
551 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
552 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
555 void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
557 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
558 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
559 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
560 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
561 sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
562 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
565 void XclExpPCField::Finalize()
567 // flags
568 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
569 // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
570 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
571 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
572 /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
573 for the current combination of item types is added to the flags. */
574 ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
576 // item count fields
577 maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
578 maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
579 // maFieldInfo.mnBaseItems set in InitStdGroupField()
580 maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
583 void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
585 if( IsNumGroupField() || IsDateGroupField() )
587 // SXNUMGROUP record
588 rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
589 rStrm << maNumGroupInfo;
590 rStrm.EndRecord();
592 // limits (min/max/step) for numeric grouping
593 OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
594 "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
595 maNumGroupLimits.Save( rStrm );
599 void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
601 OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
602 "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
603 if( IsStdGroupField() && !maGroupOrder.empty() )
605 rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
606 for( const auto& rItem : maGroupOrder )
607 rStrm << rItem;
608 rStrm.EndRecord();
612 void XclExpPCField::WriteBody( XclExpStream& rStrm )
614 rStrm << maFieldInfo;
617 XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
618 XclExpRoot( rRoot ),
619 mnListIdx( nListIdx ),
620 mbValid( false )
622 // source from sheet only
623 const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
624 if(!pSrcDesc)
625 return;
627 /* maOrigSrcRange: Range received from the DataPilot object.
628 maExpSrcRange: Range written to the DCONREF record.
629 maDocSrcRange: Range used to get source data from Calc document.
630 This range may be shorter than maExpSrcRange to improve export
631 performance (#i22541#). */
632 maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
633 maSrcRangeName = pSrcDesc->GetRangeName();
635 // internal sheet data only
636 SCTAB nScTab = maExpSrcRange.aStart.Tab();
637 if( !((nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab )) )
638 return;
640 // ValidateRange() restricts source range to valid Excel limits
641 if( !GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
642 return;
644 // #i22541# skip empty cell areas (performance)
645 SCCOL nDocCol1, nDocCol2;
646 SCROW nDocRow1, nDocRow2;
647 GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
648 GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
649 SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
650 SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
651 SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
652 SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
654 // #i22541# do not store index list for too big ranges
655 if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
656 ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
658 // adjust row indexes, keep one row of empty area to surely have the empty cache item
659 if( nSrcRow1 < nDocRow1 )
660 nSrcRow1 = nDocRow1 - 1;
661 if( nSrcRow2 > nDocRow2 )
662 nSrcRow2 = nDocRow2 + 1;
664 maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
665 maDocSrcRange.aStart.SetRow( nSrcRow1 );
666 maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
667 maDocSrcRange.aEnd.SetRow( nSrcRow2 );
669 GetDoc().GetName( nScTab, maTabName );
670 maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
671 maPCInfo.mnStrmId = nListIdx + 1;
672 maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
674 AddFields( rDPObj );
676 mbValid = true;
679 bool XclExpPivotCache::HasItemIndexList() const
681 return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
684 sal_uInt16 XclExpPivotCache::GetFieldCount() const
686 return static_cast< sal_uInt16 >( maFieldList.GetSize() );
689 const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
691 return maFieldList.GetRecord( nFieldIdx );
694 bool XclExpPivotCache::HasAddFields() const
696 // pivot cache can be shared, if there are no additional cache fields
697 return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
700 bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
702 /* For now, only sheet sources are supported, therefore it is enough to
703 compare the ScSheetSourceDesc. Later, there should be done more complicated
704 comparisons regarding the source type of rDPObj and this cache. */
705 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
706 return pSrcDesc->GetSourceRange() == maOrigSrcRange;
707 return false;
710 void XclExpPivotCache::Save( XclExpStream& rStrm )
712 OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
713 // SXIDSTM
714 XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
715 // SXVS
716 XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
718 if (!maSrcRangeName.isEmpty())
719 // DCONNAME
720 WriteDConName(rStrm);
721 else
722 // DCONREF
723 WriteDconref(rStrm);
725 // create the pivot cache storage stream
726 WriteCacheStream();
729 void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
733 void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
735 AddStdFields( rDPObj );
736 maPCInfo.mnStdFields = GetFieldCount();
737 AddGroupFields( rDPObj );
738 maPCInfo.mnTotalFields = GetFieldCount();
741 void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
743 // if item index list is not written, used shortened source range (maDocSrcRange) for performance
744 const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
745 // create a standard pivot cache field for each source column
746 for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
748 ScRange aColRange( rRange );
749 aColRange.aStart.SetCol( nScCol );
750 aColRange.aEnd.SetCol( nScCol );
751 maFieldList.AppendNewRecord( new XclExpPCField(
752 GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
756 void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
758 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
759 if(!pSaveData)
760 return;
761 const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData();
762 if( !pSaveDimData )
763 return;
765 // loop over all existing standard fields to find their group fields
766 for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
768 if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ) )
770 const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
771 XclExpPCField* pLastGroupField = pCurrStdField;
772 while( pGroupDim )
774 // insert the new grouping field
775 XclExpPCFieldRef xNewGroupField = new XclExpPCField(
776 GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField );
777 maFieldList.AppendRecord( xNewGroupField );
779 // register new grouping field at current grouping field, building a chain
780 pLastGroupField->SetGroupChildField( *xNewGroupField );
782 // next grouping dimension
783 pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
784 pLastGroupField = xNewGroupField.get();
790 void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
792 XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), OUString(), &maTabName ) );
793 rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
794 rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
795 << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
796 << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
797 << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
798 << aRef
799 << sal_uInt8( 0 );
800 rStrm.EndRecord();
803 void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
805 XclExpString aName(maSrcRangeName);
806 rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
807 rStrm << aName << sal_uInt16(0);
808 rStrm.EndRecord();
811 void XclExpPivotCache::WriteCacheStream()
813 tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
814 tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
815 if( !xSvStrm.is() )
816 return;
818 XclExpStream aStrm( *xSvStrm, GetRoot() );
819 // SXDB
820 WriteSxdb( aStrm );
821 // SXDBEX
822 WriteSxdbex( aStrm );
823 // field list (SXFIELD and items)
824 maFieldList.Save( aStrm );
825 // index table (list of SXINDEXLIST)
826 WriteSxindexlistList( aStrm );
827 // EOF
828 XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
831 void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
833 rStrm.StartRecord( EXC_ID_SXDB, 21 );
834 rStrm << maPCInfo;
835 rStrm.EndRecord();
838 void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
840 rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
841 rStrm << EXC_SXDBEX_CREATION_DATE
842 << sal_uInt32( 0 ); // number of SXFORMULA records
843 rStrm.EndRecord();
846 void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
848 if( !HasItemIndexList() )
849 return;
851 std::size_t nRecSize = 0;
852 size_t nPos, nSize = maFieldList.GetSize();
853 for( nPos = 0; nPos < nSize; ++nPos )
854 nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
856 for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
858 rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
859 for( nPos = 0; nPos < nSize; ++nPos )
860 maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
861 rStrm.EndRecord();
865 // Pivot table
867 namespace {
869 /** Returns a display string for a data field containing the field name and aggregation function. */
870 OUString lclGetDataFieldCaption( std::u16string_view rFieldName, ScGeneralFunction eFunc )
872 OUString aCaption;
874 TranslateId pResIdx;
875 switch( eFunc )
877 case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
878 case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
879 case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
880 case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
881 case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
882 case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
883 case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
884 case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
885 case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
886 case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
887 case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
888 default:;
890 if (pResIdx)
891 aCaption = ScResId(pResIdx) + " - ";
892 aCaption += rFieldName;
893 return aCaption;
896 } // namespace
898 XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
899 XclExpRecord( EXC_ID_SXVI, 8 ),
900 mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
902 maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
903 maItemInfo.mnCacheIdx = nCacheIdx;
904 maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
907 XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
908 XclExpRecord( EXC_ID_SXVI, 8 ),
909 mpCacheItem( nullptr )
911 maItemInfo.mnType = nItemType;
912 maItemInfo.mnCacheIdx = nCacheIdx;
913 maItemInfo.maVisName.mbUseCache = true;
916 OUString XclExpPTItem::GetItemName() const
918 return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
921 void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
923 // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
924 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
925 // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
926 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
928 // visible name
929 const std::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
930 if (pVisName && *pVisName != GetItemName())
931 maItemInfo.SetVisName(*pVisName);
934 void XclExpPTItem::WriteBody( XclExpStream& rStrm )
936 rStrm << maItemInfo;
939 XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
940 mrPTable( rPTable ),
941 mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
943 maFieldInfo.mnCacheIdx = nCacheIdx;
945 // create field items
946 if( mpCacheField )
947 for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
948 maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
949 maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
952 // data access ----------------------------------------------------------------
954 OUString XclExpPTField::GetFieldName() const
956 return mpCacheField ? mpCacheField->GetFieldName() : OUString();
959 sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
961 OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
962 // will return 0xFFFF for empty vector -> ok
963 return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
966 sal_uInt16 XclExpPTField::GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const
968 for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
969 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
970 return static_cast< sal_uInt16 >( nPos );
971 return nDefaultIdx;
974 // fill data --------------------------------------------------------------
977 * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
978 * are not escaped at all.
980 static OUString lcl_convertCalcSubtotalName(const OUString& rName)
982 OUStringBuffer aBuf;
983 const sal_Unicode* p = rName.getStr();
984 sal_Int32 n = rName.getLength();
985 bool bEscaped = false;
986 for (sal_Int32 i = 0; i < n; ++i)
988 const sal_Unicode c = p[i];
989 if (!bEscaped && c == '\\')
991 bEscaped = true;
992 continue;
995 aBuf.append(c);
996 bEscaped = false;
998 return aBuf.makeStringAndClear();
1001 void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1003 // orientation
1004 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1005 OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1006 maFieldInfo.AddApiOrient( eOrient );
1008 // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1009 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1011 // visible name
1012 const std::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
1013 if (pLayoutName && *pLayoutName != GetFieldName())
1014 maFieldInfo.SetVisName(*pLayoutName);
1016 const std::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
1017 if (pSubtotalName)
1019 OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1020 maFieldExtInfo.mpFieldTotalName = aSubName;
1023 // subtotals
1024 XclPTSubtotalVec aSubtotals;
1025 aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1026 for( tools::Long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1027 aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1028 maFieldInfo.SetSubtotals( aSubtotals );
1030 // sorting
1031 if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1033 maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1034 if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
1035 maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1036 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1039 // auto show
1040 if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1042 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1043 maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1044 maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1045 maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1048 // layout
1049 if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1051 maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1052 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1055 // special page field properties
1056 if( eOrient == DataPilotFieldOrientation_PAGE )
1058 maPageInfo.mnField = GetFieldIndex();
1059 maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1062 // item properties
1063 const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1064 for (const auto& pMember : rMembers)
1065 if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
1066 pItem->SetPropertiesFromMember( *pMember );
1069 void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1071 maDataInfoVec.emplace_back( );
1072 XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1073 rDataInfo.mnField = GetFieldIndex();
1075 // orientation
1076 maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1078 // aggregation function
1079 ScGeneralFunction eFunc = rSaveDim.GetFunction();
1080 rDataInfo.SetApiAggFunc( eFunc );
1082 // visible name
1083 const std::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
1084 if (pVisName)
1085 rDataInfo.SetVisName(*pVisName);
1086 else
1087 rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1089 // result field reference
1090 if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1092 rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1093 rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1094 if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1096 rDataInfo.mnRefField = pRefField->GetFieldIndex();
1097 if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
1098 rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1103 void XclExpPTField::AppendSubtotalItems()
1105 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1106 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1107 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1108 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1109 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1110 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1111 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1112 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1113 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1114 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1115 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1116 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1119 // records --------------------------------------------------------------------
1121 void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1123 rStrm << maPageInfo;
1126 void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1128 OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1129 if( nDataInfoIdx < maDataInfoVec.size() )
1131 rStrm.StartRecord( EXC_ID_SXDI, 12 );
1132 rStrm << maDataInfoVec[ nDataInfoIdx ];
1133 rStrm.EndRecord();
1137 void XclExpPTField::Save( XclExpStream& rStrm )
1139 // SXVD
1140 WriteSxvd( rStrm );
1141 // list of SXVI records
1142 maItemList.Save( rStrm );
1143 // SXVDEX
1144 WriteSxvdex( rStrm );
1147 // private --------------------------------------------------------------------
1149 XclExpPTItem* XclExpPTField::GetItemAcc( std::u16string_view rName )
1151 XclExpPTItem* pItem = nullptr;
1152 for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1153 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1154 pItem = maItemList.GetRecord( nPos );
1155 return pItem;
1158 void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1160 maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
1161 ++maFieldInfo.mnItemCount;
1164 void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1166 rStrm.StartRecord( EXC_ID_SXVD, 10 );
1167 rStrm << maFieldInfo;
1168 rStrm.EndRecord();
1171 void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1173 rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1174 rStrm << maFieldExtInfo;
1175 rStrm.EndRecord();
1178 XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
1179 XclExpRoot( rRoot ),
1180 mrPCache( rPCache ),
1181 maDataOrientField( *this, EXC_SXIVD_DATA ),
1182 mnOutScTab( 0 ),
1183 mbValid( false ),
1184 mbFilterBtn( false )
1186 const ScRange& rOutScRange = rDPObj.GetOutRange();
1187 if( !GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1188 return;
1190 // DataPilot properties -----------------------------------------------
1192 // pivot table properties from DP object
1193 mnOutScTab = rOutScRange.aStart.Tab();
1194 maPTInfo.maTableName = rDPObj.GetName();
1195 maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1197 maPTViewEx9Info.Init( rDPObj );
1199 const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
1200 if( !pSaveData )
1201 return;
1203 // additional properties from ScDPSaveData
1204 SetPropertiesFromDP( *pSaveData );
1206 // loop over all dimensions ---------------------------------------
1208 /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1209 for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1210 maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1212 const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
1214 /* 2) First process all data dimensions, they are needed for extended
1215 settings of row/column/page fields (sorting/auto show). */
1216 for (auto const& iter : rDimList)
1218 if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
1219 SetDataFieldPropertiesFromDim(*iter);
1222 /* 3) Row/column/page/hidden fields. */
1223 for (auto const& iter : rDimList)
1225 if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
1226 SetFieldPropertiesFromDim(*iter);
1229 // Finalize -------------------------------------------------------
1231 Finalize();
1232 mbValid = true;
1235 const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1237 return mrPCache.GetField( nCacheIdx );
1240 const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1242 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx );
1245 const XclExpPTField* XclExpPivotTable::GetField( std::u16string_view rName ) const
1247 return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1250 sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
1252 auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
1253 [this, &rName](const XclPTDataFieldPos& rDataField) {
1254 const XclExpPTField* pField = GetField( rDataField.first );
1255 return pField && pField->GetFieldName() == rName;
1257 if (aIt != maDataFields.end())
1258 return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
1259 return nDefaultIdx;
1262 void XclExpPivotTable::Save( XclExpStream& rStrm )
1264 if( !mbValid )
1265 return;
1267 // SXVIEW
1268 WriteSxview( rStrm );
1269 // pivot table fields (SXVD, SXVDEX, and item records)
1270 maFieldList.Save( rStrm );
1271 // SXIVD records for row and column fields
1272 WriteSxivd( rStrm, maRowFields );
1273 WriteSxivd( rStrm, maColFields );
1274 // SXPI
1275 WriteSxpi( rStrm );
1276 // list of SXDI records containing data field info
1277 WriteSxdiList( rStrm );
1278 // SXLI records
1279 WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1280 WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1281 // SXEX
1282 WriteSxex( rStrm );
1283 // QSISXTAG
1284 WriteQsiSxTag( rStrm );
1285 // SXVIEWEX9
1286 WriteSxViewEx9( rStrm );
1289 XclExpPTField* XclExpPivotTable::GetFieldAcc( std::u16string_view rName )
1291 XclExpPTField* pField = nullptr;
1292 for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1293 if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1294 pField = maFieldList.GetRecord( nPos );
1295 return pField;
1298 XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1300 // data field orientation field?
1301 if( rSaveDim.IsDataLayout() )
1302 return &maDataOrientField;
1304 // a real dimension
1305 OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
1306 return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
1309 // fill data --------------------------------------------------------------
1311 void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1313 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1314 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1315 ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1316 mbFilterBtn = rSaveData.GetFilterButton();
1317 const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1319 if (pDim && pDim->GetLayoutName())
1320 maPTInfo.maDataName = *pDim->GetLayoutName();
1321 else
1322 maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
1325 void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1327 XclExpPTField* pField = GetFieldAcc( rSaveDim );
1328 if(!pField)
1329 return;
1331 // field properties
1332 pField->SetPropertiesFromDim( rSaveDim );
1334 // update the corresponding field position list
1335 DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
1336 sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1337 bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1338 bool bMultiData = maDataFields.size() > 1;
1340 if( bDataLayout && !bMultiData )
1341 return;
1343 switch( eOrient )
1345 case DataPilotFieldOrientation_ROW:
1346 maRowFields.push_back( nFieldIdx );
1347 if( bDataLayout )
1348 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1349 break;
1350 case DataPilotFieldOrientation_COLUMN:
1351 maColFields.push_back( nFieldIdx );
1352 if( bDataLayout )
1353 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1354 break;
1355 case DataPilotFieldOrientation_PAGE:
1356 maPageFields.push_back( nFieldIdx );
1357 OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1358 break;
1359 case DataPilotFieldOrientation_DATA:
1360 OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1361 break;
1362 default:;
1366 void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1368 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1370 // field properties
1371 pField->SetDataPropertiesFromDim( rSaveDim );
1372 // update the data field position list
1373 maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
1377 void XclExpPivotTable::Finalize()
1379 // field numbers
1380 maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1381 maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1382 maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1383 maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1384 maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1386 maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1387 maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1389 // subtotal items
1390 for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1391 maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1393 // find data field orientation field
1394 maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1395 const ScfUInt16Vec* pFieldVec = nullptr;
1396 switch( maPTInfo.mnDataAxis )
1398 case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1399 case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1402 if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1404 ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1405 if( aIt != pFieldVec->end() )
1406 maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
1409 // single data field is always row oriented
1410 if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1411 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1413 // update output range (initialized in ctor)
1414 sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1415 sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1416 sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1417 sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1418 // exclude page fields from output range
1419 rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1420 // exclude filter button from output range
1421 if( mbFilterBtn )
1422 ++rnXclRow1;
1423 // exclude empty row between (filter button and/or page fields) and table
1424 if( mbFilterBtn || maPTInfo.mnPageFields )
1425 ++rnXclRow1;
1427 // data area
1428 sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1429 sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1430 rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1431 rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1432 if( maDataFields.empty() )
1433 ++rnDataXclRow;
1435 bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1436 if (bExtraHeaderRow)
1437 // Insert an extra row only when there is no column field.
1438 ++rnDataXclRow;
1440 rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1441 rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1442 maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1443 maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1445 // first heading
1446 maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
1447 if (bExtraHeaderRow)
1448 maPTInfo.mnFirstHeadRow += 1;
1451 // records ----------------------------------------------------------------
1453 void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1455 rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
1456 rStrm << maPTInfo;
1457 rStrm.EndRecord();
1460 void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
1462 if( !rFields.empty() )
1464 rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1465 for( const auto& rField : rFields )
1466 rStrm << rField;
1467 rStrm.EndRecord();
1471 void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1473 if( !maPageFields.empty() )
1475 rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1476 rStrm.SetSliceSize( 6 );
1477 for( const auto& rPageField : maPageFields )
1479 XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
1480 if( xField )
1481 xField->WriteSxpiEntry( rStrm );
1483 rStrm.EndRecord();
1487 void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1489 for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
1491 XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
1492 if( xField )
1493 xField->WriteSxdi( rStrm, rDataInfoIdx );
1497 void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
1499 if( nLineCount <= 0 )
1500 return;
1502 std::size_t nLineSize = 8 + 2 * nIndexCount;
1503 rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1505 /* Excel expects the records to be filled completely, do not
1506 set a segment size... */
1507 // rStrm.SetSliceSize( nLineSize );
1509 for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1511 // Excel XP needs a partly initialized SXLI record
1512 rStrm << sal_uInt16( 0 ) // number of equal index entries
1513 << EXC_SXVI_TYPE_DATA
1514 << nIndexCount
1515 << EXC_SXLI_DEFAULTFLAGS;
1516 rStrm.WriteZeroBytes( 2 * nIndexCount );
1518 rStrm.EndRecord();
1521 void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1523 rStrm.StartRecord( EXC_ID_SXEX, 24 );
1524 rStrm << maPTExtInfo;
1525 rStrm.EndRecord();
1528 void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1530 rStrm.StartRecord( 0x0802, 32 );
1532 sal_uInt16 const nRecordType = 0x0802;
1533 sal_uInt16 const nDummyFlags = 0x0000;
1534 sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
1536 rStrm << nRecordType << nDummyFlags << nTableType;
1538 // General flags
1539 sal_uInt16 const nFlags = 0x0001;
1540 #if 0
1541 // for doc purpose
1542 sal_uInt16 nFlags = 0x0000;
1543 bool bEnableRefresh = true;
1544 bool bPCacheInvalid = false;
1545 bool bOlapPTReport = false;
1547 if (bEnableRefresh) nFlags |= 0x0001;
1548 if (bPCacheInvalid) nFlags |= 0x0002;
1549 if (bOlapPTReport) nFlags |= 0x0004;
1550 #endif
1551 rStrm << nFlags;
1553 // Feature-specific options. The value differs depending on the table
1554 // type, but we assume the table type is always pivot table.
1555 sal_uInt32 const nOptions = 0x00000000;
1556 #if 0
1557 // documentation for which bit is for what
1558 bool bNoStencil = false;
1559 bool bHideTotal = false;
1560 bool bEmptyRows = false;
1561 bool bEmptyCols = false;
1562 if (bNoStencil) nOptions |= 0x00000001;
1563 if (bHideTotal) nOptions |= 0x00000002;
1564 if (bEmptyRows) nOptions |= 0x00000008;
1565 if (bEmptyCols) nOptions |= 0x00000010;
1566 #endif
1567 rStrm << nOptions;
1569 sal_uInt8 eXclVer = 0; // Excel2000
1570 sal_uInt8 const nOffsetBytes = 16;
1571 rStrm << eXclVer // version table last refreshed
1572 << eXclVer // minimum version to refresh
1573 << nOffsetBytes
1574 << eXclVer; // first version created
1576 rStrm << XclExpString(maPTInfo.maTableName);
1577 rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1579 rStrm.EndRecord();
1582 void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1584 // Until we sync the autoformat ids only export if using grid header layout
1585 // That could only have been set via xls import so far.
1586 if ( 0 == maPTViewEx9Info.mnGridLayout )
1588 rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1589 rStrm << maPTViewEx9Info;
1590 rStrm.EndRecord();
1594 namespace {
1596 const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1598 /** Record wrapper class to write the pivot caches or pivot tables. */
1599 class XclExpPivotRecWrapper : public XclExpRecordBase
1601 public:
1602 explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1603 virtual void Save( XclExpStream& rStrm ) override;
1604 private:
1605 XclExpPivotTableManager& mrPTMgr;
1606 SCTAB mnScTab;
1609 XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1610 mrPTMgr( rPTMgr ),
1611 mnScTab( nScTab )
1615 void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1617 if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1618 mrPTMgr.WritePivotCaches( rStrm );
1619 else
1620 mrPTMgr.WritePivotTables( rStrm, mnScTab );
1623 } // namespace
1625 XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1626 XclExpRoot( rRoot )
1630 void XclExpPivotTableManager::CreatePivotTables()
1632 if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1633 for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1635 ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
1636 if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
1637 maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
1641 XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1643 return new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES );
1646 XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1648 return new XclExpPivotRecWrapper( *this, nScTab );
1651 void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1653 maPCacheList.Save( rStrm );
1656 void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1658 for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1660 XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1661 if( xPTable->GetScTab() == nScTab )
1662 xPTable->Save( rStrm );
1666 const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1668 // try to find a pivot cache with the same data source
1669 /* #i25110# In Excel, the pivot cache contains additional fields
1670 (i.e. grouping info, calculated fields). If the passed DataPilot object
1671 or the found cache contains this data, do not share the cache with
1672 multiple pivot tables. */
1673 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1675 const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1676 // no dimension save data at all or save data does not contain grouping info
1677 if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1679 // check all existing pivot caches
1680 for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1682 XclExpPivotCache* pPCache = maPCacheList.GetRecord( nPos );
1683 // pivot cache does not have grouping info and source data is equal
1684 if( !pPCache->HasAddFields() && pPCache->HasEqualDataSource( rDPObj ) )
1685 return pPCache;
1690 // create a new pivot cache
1691 sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1692 XclExpPivotCacheRef xNewPCache = new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx );
1693 if( xNewPCache->IsValid() )
1695 maPCacheList.AppendRecord( xNewPCache.get() );
1696 return xNewPCache.get();
1699 return nullptr;
1702 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */