Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xipivot.cxx
blob111811e4c361d1106414fe5047acb987b5a35bc2
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 <xipivot.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>
28 #include <tools/datetime.hxx>
29 #include <svl/intitem.hxx>
30 #include <sal/log.hxx>
31 #include <sot/storage.hxx>
32 #include <unotools/configmgr.hxx>
34 #include <document.hxx>
35 #include <formulacell.hxx>
36 #include <dpsave.hxx>
37 #include <dpdimsave.hxx>
38 #include <dpobject.hxx>
39 #include <dpshttab.hxx>
40 #include <dpoutputgeometry.hxx>
41 #include <scitems.hxx>
42 #include <attrib.hxx>
44 #include <xltracer.hxx>
45 #include <xistream.hxx>
46 #include <xihelper.hxx>
47 #include <xilink.hxx>
48 #include <xiescher.hxx>
50 //TODO ExcelToSc usage
51 #include <excform.hxx>
52 #include <documentimport.hxx>
54 #include <vector>
56 using namespace com::sun::star;
58 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
59 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
60 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
61 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
62 using ::com::sun::star::sheet::DataPilotFieldReference;
63 using ::std::vector;
65 // Pivot cache
67 XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
69 switch( rStrm.GetRecId() )
71 case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
72 case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
73 case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
74 case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
75 case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
76 case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
77 case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
78 default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
82 namespace {
84 void lclSetValue( XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, SvNumFormatType nFormatType )
86 ScDocumentImport& rDoc = rRoot.GetDocImport();
87 rDoc.setNumericCell(rScPos, fValue);
88 sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
89 rDoc.getDoc().ApplyAttr(
90 rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT, nScNumFmt));
93 } // namespace
95 void XclImpPCItem::WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const
97 ScDocumentImport& rDoc = rRoot.GetDocImport();
98 if( const OUString* pText = GetText() )
99 rDoc.setStringCell(rScPos, *pText);
100 else if( const double* pfValue = GetDouble() )
101 rDoc.setNumericCell(rScPos, *pfValue);
102 else if( const sal_Int16* pnValue = GetInteger() )
103 rDoc.setNumericCell(rScPos, *pnValue);
104 else if( const bool* pbValue = GetBool() )
105 lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, SvNumFormatType::LOGICAL );
106 else if( const DateTime* pDateTime = GetDateTime() )
108 // set number format date, time, or date/time, depending on the value
109 double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
110 double fInt = 0.0;
111 double fFrac = modf( fValue, &fInt );
112 SvNumFormatType nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? SvNumFormatType::DATE :
113 ((fInt == 0.0) ? SvNumFormatType::TIME : SvNumFormatType::DATETIME);
114 lclSetValue( rRoot, rScPos, fValue, nFormatType );
116 else if( const sal_uInt16* pnError = GetError() )
118 double fValue;
119 sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
120 std::unique_ptr<ScTokenArray> pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
121 XclTools::ErrorToEnum( fValue, true, nErrCode ) );
122 ScFormulaCell* pCell = pScTokArr
123 ? new ScFormulaCell(&rDoc.getDoc(), rScPos, std::move(pScTokArr))
124 : new ScFormulaCell(&rDoc.getDoc(), rScPos);
125 pCell->SetHybridDouble( fValue );
126 rDoc.setFormulaCell(rScPos, pCell);
130 void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
132 OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
133 SetDouble( rStrm.ReadDouble() );
136 void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
138 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
139 SetBool( rStrm.ReaduInt16() != 0 );
142 void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
144 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
145 SetError( rStrm.ReaduInt16() );
148 void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
150 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
151 SetInteger( rStrm.ReadInt16() );
154 void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
156 OSL_ENSURE( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
157 SetText( rStrm.ReadUniString() );
160 void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
162 OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
163 sal_uInt16 nYear, nMonth;
164 sal_uInt8 nDay, nHour, nMin, nSec;
165 nYear = rStrm.ReaduInt16();
166 nMonth = rStrm.ReaduInt16();
167 nDay = rStrm.ReaduInt8();
168 nHour = rStrm.ReaduInt8();
169 nMin = rStrm.ReaduInt8();
170 nSec = rStrm.ReaduInt8();
171 SetDateTime( DateTime( Date( nDay, nMonth, nYear ), tools::Time( nHour, nMin, nSec ) ) );
174 void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
176 OSL_ENSURE( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
177 SetEmpty();
180 XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
181 XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
182 XclImpRoot( rRoot ),
183 mrPCache( rPCache ),
184 mnSourceScCol( -1 ),
185 mbNumGroupInfoRead( false )
189 XclImpPCField::~XclImpPCField()
193 // general field/item access --------------------------------------------------
195 const OUString& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
197 if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
199 const OUString& rVisName = rVisNames[ mnFieldIdx ];
200 if (!rVisName.isEmpty())
201 return rVisName;
203 return maFieldInfo.maName;
206 const XclImpPCField* XclImpPCField::GetGroupBaseField() const
208 OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
209 return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : nullptr;
212 const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
214 return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
217 const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
219 OSL_ENSURE( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
220 OSL_ENSURE( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
221 return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : nullptr;
224 void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab )
226 OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
227 GetDocImport().setStringCell(ScAddress(nScCol, 0, nScTab), maFieldInfo.maName);
228 mnSourceScCol = nScCol;
231 void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx )
233 if( nItemIdx < maOrigItems.size() )
234 maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
237 void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab )
239 if( !maOrigItems.empty() )
240 maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
243 // records --------------------------------------------------------------------
245 void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
247 rStrm >> maFieldInfo;
249 /* Detect the type of this field. This is done very restrictive to detect
250 any unexpected state. */
251 meFieldType = EXC_PCFIELD_UNKNOWN;
253 bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
254 bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
255 bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
256 bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
257 bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
259 sal_uInt16 nVisC = maFieldInfo.mnVisItems;
260 sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
261 sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
262 sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
263 OSL_ENSURE( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
265 sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
266 bool bType =
267 (nType == EXC_SXFIELD_DATA_STR) ||
268 (nType == EXC_SXFIELD_DATA_INT) ||
269 (nType == EXC_SXFIELD_DATA_DBL) ||
270 (nType == EXC_SXFIELD_DATA_STR_INT) ||
271 (nType == EXC_SXFIELD_DATA_STR_DBL) ||
272 (nType == EXC_SXFIELD_DATA_DATE) ||
273 (nType == EXC_SXFIELD_DATA_DATE_EMP) ||
274 (nType == EXC_SXFIELD_DATA_DATE_NUM) ||
275 (nType == EXC_SXFIELD_DATA_DATE_STR);
276 bool bTypeNone =
277 (nType == EXC_SXFIELD_DATA_NONE);
278 // for now, ignore data type of calculated fields
279 OSL_ENSURE( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
281 if( nVisC > 0 || bPostp )
283 if( bItems && !bPostp )
285 if( !bCalced )
287 // 1) standard fields and standard grouping fields
288 if( !bNum )
290 // 1a) standard field without grouping
291 if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
292 meFieldType = EXC_PCFIELD_STANDARD;
294 // 1b) standard grouping field
295 else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
296 meFieldType = EXC_PCFIELD_STDGROUP;
298 // 2) numerical grouping fields
299 else if( (nGroupC == nVisC) && (nBaseC == 0) )
301 // 2a) single num/date grouping field without child grouping field
302 if( !bChild && bType && (nOrigC > 0) )
304 switch( nType )
306 case EXC_SXFIELD_DATA_INT:
307 case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
308 case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
309 default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
313 // 2b) first date grouping field with child grouping field
314 else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
315 meFieldType = EXC_PCFIELD_DATEGROUP;
317 // 2c) additional date grouping field
318 else if( bTypeNone && (nOrigC == 0) )
319 meFieldType = EXC_PCFIELD_DATECHILD;
321 OSL_ENSURE( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
324 // 3) calculated field
325 else
327 if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
328 meFieldType = EXC_PCFIELD_CALCED;
329 OSL_ENSURE( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
333 else if( !bItems && bPostp )
335 // 4) standard field with postponed items
336 if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
337 meFieldType = EXC_PCFIELD_STANDARD;
338 OSL_ENSURE( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
343 void XclImpPCField::ReadItem( XclImpStream& rStrm )
345 OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
347 // read the item
348 XclImpPCItemRef xItem( new XclImpPCItem( rStrm ) );
350 // try to insert into an item list
351 if( mbNumGroupInfoRead )
353 // there are 3 items after SXNUMGROUP that contain grouping limits and step count
354 if( maNumGroupItems.size() < 3 )
355 maNumGroupItems.push_back( xItem );
356 else
357 maOrigItems.push_back( xItem );
359 else if( HasInlineItems() || HasPostponedItems() )
361 maItems.push_back( xItem );
362 // visible item is original item in standard fields
363 if( IsStandardField() )
364 maOrigItems.push_back( xItem );
368 void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
370 OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
371 OSL_ENSURE( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
372 OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
373 rStrm >> maNumGroupInfo;
374 mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
377 void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
379 OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
380 OSL_ENSURE( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
381 OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
382 OSL_ENSURE( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
383 maGroupOrder.clear();
384 size_t nSize = rStrm.GetRecLeft() / 2;
385 maGroupOrder.resize( nSize, 0 );
386 for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
387 maGroupOrder[ nIdx ] = rStrm.ReaduInt16();
390 // grouping -------------------------------------------------------------------
392 void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
394 if (!GetFieldName(rVisNames).isEmpty())
396 if( IsStdGroupField() )
397 ConvertStdGroupField( rSaveData, rVisNames );
398 else if( IsNumGroupField() )
399 ConvertNumGroupField( rSaveData, rVisNames );
400 else if( IsDateGroupField() )
401 ConvertDateGroupField( rSaveData, rVisNames );
405 // private --------------------------------------------------------------------
407 void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
409 if( const XclImpPCField* pBaseField = GetGroupBaseField() )
411 const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
412 if( !rBaseFieldName.isEmpty() )
414 // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
415 ScDPSaveGroupItemVec aGroupItems;
416 aGroupItems.reserve( maItems.size() );
417 // initialize with own item names
418 for( const auto& rxItem : maItems )
419 aGroupItems.emplace_back( rxItem->ConvertToText() );
421 // *** iterate over all base items, set their names at corresponding own items ***
422 for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
423 if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
424 if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
425 if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
426 if( *pBaseItem != *pGroupItem )
427 aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
429 // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
430 ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
431 for( const auto& rGroupItem : aGroupItems )
432 if( !rGroupItem.IsEmpty() )
433 aGroupDim.AddGroupItem( rGroupItem );
434 rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
439 void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
441 ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
442 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
443 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
446 void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
448 ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
449 sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
451 switch( meFieldType )
453 case EXC_PCFIELD_DATEGROUP:
455 if( aDateInfo.mbDateValues )
457 // special case for days only with step value - create numeric grouping
458 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
459 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
461 else
463 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
464 aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
465 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
468 break;
470 case EXC_PCFIELD_DATECHILD:
472 if( const XclImpPCField* pBaseField = GetGroupBaseField() )
474 const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
475 if( !rBaseFieldName.isEmpty() )
477 ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
478 aGroupDim.SetDateInfo( aDateInfo, nScDateType );
479 rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
483 break;
485 default:
486 OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
490 ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
492 ScDPNumGroupInfo aNumInfo;
493 aNumInfo.mbEnable = true;
494 aNumInfo.mbDateValues = false;
495 aNumInfo.mbAutoStart = true;
496 aNumInfo.mbAutoEnd = true;
498 if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
500 aNumInfo.mfStart = *pfMinValue;
501 aNumInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
503 if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
505 aNumInfo.mfEnd = *pfMaxValue;
506 aNumInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
508 if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
509 aNumInfo.mfStep = *pfStepValue;
511 return aNumInfo;
514 ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
516 ScDPNumGroupInfo aDateInfo;
517 aDateInfo.mbEnable = true;
518 aDateInfo.mbDateValues = false;
519 aDateInfo.mbAutoStart = true;
520 aDateInfo.mbAutoEnd = true;
522 if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
524 aDateInfo.mfStart = GetDoubleFromDateTime( *pMinDate );
525 aDateInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
527 if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
529 aDateInfo.mfEnd = GetDoubleFromDateTime( *pMaxDate );
530 aDateInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
532 // GetDateGroupStep() returns a value for date type "day" in single date groups only
533 if( const sal_Int16* pnStepValue = GetDateGroupStep() )
535 aDateInfo.mfStep = *pnStepValue;
536 aDateInfo.mbDateValues = true;
539 return aDateInfo;
542 const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
544 OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
545 if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
547 OSL_ENSURE( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
548 return pItem->GetDouble();
550 return nullptr;
553 const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
555 OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
556 if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
558 OSL_ENSURE( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
559 return pItem->GetDateTime();
561 return nullptr;
564 const sal_Int16* XclImpPCField::GetDateGroupStep() const
566 // only for single date grouping fields, not for grouping chains
567 if( !IsGroupBaseField() && !IsGroupChildField() )
569 // only days may have a step value, return 0 for all other date types
570 if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
572 if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
574 OSL_ENSURE( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
575 if( const sal_Int16* pnStep = pItem->GetInteger() )
577 OSL_ENSURE( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
578 // return nothing for step count 1 - this is also a standard date group in Excel
579 return (*pnStep > 1) ? pnStep : nullptr;
584 return nullptr;
587 XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
588 XclImpRoot( rRoot ),
589 maSrcRange( ScAddress::INITIALIZE_INVALID ),
590 mnStrmId( 0 ),
591 mnSrcType( EXC_SXVS_UNKNOWN ),
592 mbSelfRef( false )
596 XclImpPivotCache::~XclImpPivotCache()
600 // data access ----------------------------------------------------------------
602 const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
604 return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
607 // records --------------------------------------------------------------------
609 void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
611 mnStrmId = rStrm.ReaduInt16();
614 void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
616 mnSrcType = rStrm.ReaduInt16();
617 GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
620 void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
622 /* Read DCONREF only once (by checking maTabName), there may be other
623 DCONREF records in another context. Read reference only if a leading
624 SXVS record is present (by checking mnSrcType). */
625 if( !maTabName.isEmpty() || (mnSrcType != EXC_SXVS_SHEET) )
626 return;
628 XclRange aXclRange( ScAddress::UNINITIALIZED );
629 aXclRange.Read( rStrm, false );
630 OUString aEncUrl = rStrm.ReadUniString();
632 XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
634 /* Do not convert maTabName to Calc sheet name -> original name is used to
635 find the sheet in the document. Sheet index of source range will be
636 found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
637 may not exist yet. */
638 if( mbSelfRef )
639 GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
642 void XclImpPivotCache::ReadDConName( XclImpStream& rStrm )
644 maSrcRangeName = rStrm.ReadUniString();
646 // This 2-byte value equals the length of string that follows, or if 0 it
647 // indicates that the name has a workbook scope. For now, we only support
648 // internal defined name with a workbook scope.
649 sal_uInt16 nFlag;
650 nFlag = rStrm.ReaduInt16();
651 mbSelfRef = (nFlag == 0);
653 if (!mbSelfRef)
654 // External name is not supported yet.
655 maSrcRangeName.clear();
658 void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream& rStrm )
660 if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
661 return;
663 ScDocument& rDoc = GetDoc();
664 SCCOL nFieldScCol = 0; // column index of source data for next field
665 SCROW nItemScRow = 0; // row index of source data for current items
666 SCTAB nScTab = 0; // sheet index of source data
667 bool bGenerateSource = false; // true = write source data from cache to dummy table
669 if( mbSelfRef )
671 if (maSrcRangeName.isEmpty())
673 // try to find internal sheet containing the source data
674 nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
675 if( rDoc.HasTable( nScTab ) )
677 // set sheet index to source range
678 maSrcRange.aStart.SetTab( nScTab );
679 maSrcRange.aEnd.SetTab( nScTab );
681 else
683 // create dummy sheet for deleted internal sheet
684 bGenerateSource = true;
688 else
690 // create dummy sheet for external sheet
691 bGenerateSource = true;
694 // create dummy sheet for source data from external or deleted sheet
695 if( bGenerateSource )
697 if( rDoc.GetTableCount() >= MAXTABCOUNT )
698 // cannot create more sheets -> exit
699 return;
701 nScTab = rDoc.GetTableCount();
702 rDoc.MakeTable( nScTab );
703 OUStringBuffer aDummyName("DPCache");
704 if( maTabName.getLength() > 0 )
705 aDummyName.append( '_' ).append( maTabName );
706 OUString aName = aDummyName.makeStringAndClear();
707 rDoc.CreateValidTabName( aName );
708 rDoc.RenameTab( nScTab, aName );
709 // set sheet index to source range
710 maSrcRange.aStart.SetTab( nScTab );
711 maSrcRange.aEnd.SetTab( nScTab );
714 // open pivot cache storage stream
715 tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
716 tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
717 if( !xSvStrm.is() )
718 return;
720 // create Excel record stream object
721 XclImpStream aPCStrm( *xSvStrm, GetRoot() );
722 aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
724 XclImpPCFieldRef xCurrField; // current field for new items
725 XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
726 XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
727 size_t nPostpIdx = 0; // index to current field with postponed items
728 bool bLoop = true; // true = continue loop
730 while( bLoop && aPCStrm.StartNextRecord() )
732 switch( aPCStrm.GetRecId() )
734 case EXC_ID_EOF:
735 bLoop = false;
736 break;
738 case EXC_ID_SXDB:
739 aPCStrm >> maPCInfo;
740 break;
742 case EXC_ID_SXFIELD:
744 xCurrField.reset();
745 sal_uInt16 nNewFieldIdx = static_cast< sal_uInt16 >( maFields.size() );
746 if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
748 xCurrField.reset( new XclImpPCField( GetRoot(), *this, nNewFieldIdx ) );
749 maFields.push_back( xCurrField );
750 xCurrField->ReadSxfield( aPCStrm );
751 if( xCurrField->HasOrigItems() )
753 if( xCurrField->HasPostponedItems() )
754 aPostpFields.push_back( xCurrField );
755 else
756 aOrigFields.push_back( xCurrField );
757 // insert field name into generated source data, field remembers its column index
758 if( bGenerateSource && (nFieldScCol <= rDoc.MaxCol()) )
759 xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
761 // do not read items into invalid/postponed fields
762 if( !xCurrField->HasInlineItems() )
763 xCurrField.reset();
766 break;
768 case EXC_ID_SXINDEXLIST:
769 // read index list and insert all items into generated source data
770 if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) && (++nItemScRow <= rDoc.MaxRow()) )
772 for( const auto& rxOrigField : aOrigFields )
774 sal_uInt16 nItemIdx = rxOrigField->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
775 rxOrigField->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
778 xCurrField.reset();
779 break;
781 case EXC_ID_SXDOUBLE:
782 case EXC_ID_SXBOOLEAN:
783 case EXC_ID_SXERROR:
784 case EXC_ID_SXINTEGER:
785 case EXC_ID_SXSTRING:
786 case EXC_ID_SXDATETIME:
787 case EXC_ID_SXEMPTY:
788 if( xCurrField ) // inline items
790 xCurrField->ReadItem( aPCStrm );
792 else if( !aPostpFields.empty() ) // postponed items
794 // read postponed item
795 aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
796 // write item to source
797 if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) )
799 // start new row, if there are only postponed fields
800 if( aOrigFields.empty() && (nPostpIdx == 0) )
801 ++nItemScRow;
802 if( nItemScRow <= rDoc.MaxRow() )
803 aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
805 // get index of next postponed field
806 ++nPostpIdx;
807 if( nPostpIdx >= aPostpFields.size() )
808 nPostpIdx = 0;
810 break;
812 case EXC_ID_SXNUMGROUP:
813 if( xCurrField )
814 xCurrField->ReadSxnumgroup( aPCStrm );
815 break;
817 case EXC_ID_SXGROUPINFO:
818 if( xCurrField )
819 xCurrField->ReadSxgroupinfo( aPCStrm );
820 break;
822 // known but ignored records
823 case EXC_ID_SXRULE:
824 case EXC_ID_SXFILT:
825 case EXC_ID_00F5:
826 case EXC_ID_SXNAME:
827 case EXC_ID_SXPAIR:
828 case EXC_ID_SXFMLA:
829 case EXC_ID_SXFORMULA:
830 case EXC_ID_SXDBEX:
831 case EXC_ID_SXFDBTYPE:
832 break;
834 default:
835 SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex << aPCStrm.GetRecId() );
839 OSL_ENSURE( maPCInfo.mnTotalFields == maFields.size(),
840 "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
842 if (static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_SAVEDATA))
844 SCROW nNewEnd = maSrcRange.aStart.Row() + maPCInfo.mnSrcRecs;
845 maSrcRange.aEnd.SetRow(nNewEnd);
848 // set source range for external source data
849 if( bGenerateSource && (nFieldScCol > 0) )
851 maSrcRange.aStart.SetCol( 0 );
852 maSrcRange.aStart.SetRow( 0 );
853 // nFieldScCol points to first unused column
854 maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
855 // nItemScRow points to last used row
856 maSrcRange.aEnd.SetRow( nItemScRow );
860 bool XclImpPivotCache::IsRefreshOnLoad() const
862 return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_REFRESH_LOAD);
865 bool XclImpPivotCache::IsValid() const
867 if (!maSrcRangeName.isEmpty())
868 return true;
870 return maSrcRange.IsValid();
873 // Pivot table
875 XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) :
876 mpCacheField( pCacheField )
880 const OUString* XclImpPTItem::GetItemName() const
882 if( mpCacheField )
883 if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
884 //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
885 return pCacheItem->IsEmpty() ? nullptr : pCacheItem->GetText();
886 return nullptr;
889 std::pair<bool, OUString> XclImpPTItem::GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const
891 if(!mpCacheField)
892 return std::pair<bool, OUString>(false, OUString());
894 const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx );
895 if(!pCacheItem)
896 return std::pair<bool, OUString>(false, OUString());
898 OUString sItemName;
899 if(pCacheItem->GetType() == EXC_PCITEM_TEXT || pCacheItem->GetType() == EXC_PCITEM_ERROR)
901 const OUString* pItemName = pCacheItem->GetText();
902 if(!pItemName)
903 return std::pair<bool, OUString>(false, OUString());
904 sItemName = *pItemName;
906 else if (pCacheItem->GetType() == EXC_PCITEM_DOUBLE)
908 sItemName = pObj->GetFormattedString(rSaveDim.GetName(), *pCacheItem->GetDouble());
910 else if (pCacheItem->GetType() == EXC_PCITEM_INTEGER)
912 sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetInteger()));
914 else if (pCacheItem->GetType() == EXC_PCITEM_BOOL)
916 sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetBool()));
918 else if (pCacheItem->GetType() == EXC_PCITEM_DATETIME)
920 sItemName = pObj->GetFormattedString(rSaveDim.GetName(), rRoot.GetDoubleFromDateTime(*pCacheItem->GetDateTime()));
922 else if (pCacheItem->GetType() == EXC_PCITEM_EMPTY)
924 // sItemName is an empty string
926 else // EXC_PCITEM_INVALID
927 return std::pair<bool, OUString>(false, OUString());
929 return std::pair<bool, OUString>(true, sItemName);
932 void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
934 rStrm >> maItemInfo;
937 void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const
939 // Find member and set properties
940 std::pair<bool, OUString> aReturnedName = GetItemName(rSaveDim, pObj, rRoot);
941 if(aReturnedName.first)
943 ScDPSaveMember* pMember = rSaveDim.GetExistingMemberByName(aReturnedName.second);
944 if(pMember)
946 pMember->SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
947 pMember->SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
948 if (maItemInfo.HasVisName())
949 pMember->SetLayoutName(*maItemInfo.GetVisName());
954 XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
955 mrPTable( rPTable )
957 maFieldInfo.mnCacheIdx = nCacheIdx;
960 // general field/item access --------------------------------------------------
962 const XclImpPCField* XclImpPTField::GetCacheField() const
964 XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
965 return xPCache ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : nullptr;
968 OUString XclImpPTField::GetFieldName() const
970 const XclImpPCField* pField = GetCacheField();
971 return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : OUString();
974 OUString XclImpPTField::GetVisFieldName() const
976 const OUString* pVisName = maFieldInfo.GetVisName();
977 return pVisName ? *pVisName : OUString();
980 const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
982 return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
985 const OUString* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
987 const XclImpPTItem* pItem = GetItem( nItemIdx );
988 return pItem ? pItem->GetItemName() : nullptr;
991 // records --------------------------------------------------------------------
993 void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
995 rStrm >> maFieldInfo;
998 void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
1000 rStrm >> maFieldExtInfo;
1003 void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
1005 XclImpPTItemRef xItem( new XclImpPTItem( GetCacheField() ) );
1006 maItems.push_back( xItem );
1007 xItem->ReadSxvi( rStrm );
1010 // row/column fields ----------------------------------------------------------
1012 void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
1014 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
1015 // special data orientation field?
1016 if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
1017 rSaveData.GetDataLayoutDimension()->SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) );
1018 else
1019 ConvertRCPField( rSaveData );
1022 // page fields ----------------------------------------------------------------
1024 void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
1026 maPageInfo = rPageInfo;
1029 void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
1031 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
1032 ConvertRCPField( rSaveData );
1035 // hidden fields --------------------------------------------------------------
1037 void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
1039 OSL_ENSURE( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
1040 ConvertRCPField( rSaveData );
1043 // data fields ----------------------------------------------------------------
1045 bool XclImpPTField::HasDataFieldInfo() const
1047 return !maDataInfoVector.empty();
1050 void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
1052 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
1053 maDataInfoVector.push_back( rDataInfo );
1056 void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
1058 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
1059 OSL_ENSURE( !maDataInfoVector.empty(), "XclImpPTField::ConvertDataField - no data field info" );
1060 if (maDataInfoVector.empty())
1061 return;
1063 OUString aFieldName = GetFieldName();
1064 if (aFieldName.isEmpty())
1065 return;
1067 ScDPSaveDimension* pSaveDim = rSaveData.GetNewDimensionByName(aFieldName);
1068 if (!pSaveDim)
1070 SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName);
1071 return;
1074 auto aIt = maDataInfoVector.begin(), aEnd = maDataInfoVector.end();
1076 ConvertDataField( *pSaveDim, *aIt );
1078 // multiple data fields -> clone dimension
1079 for( ++aIt; aIt != aEnd; ++aIt )
1081 ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( *pSaveDim );
1082 ConvertDataFieldInfo( rDupDim, *aIt );
1086 // private --------------------------------------------------------------------
1089 * Convert Excel-encoded subtotal name to a Calc-encoded one.
1091 static OUString lcl_convertExcelSubtotalName(const OUString& rName)
1093 OUStringBuffer aBuf;
1094 const sal_Unicode* p = rName.getStr();
1095 sal_Int32 n = rName.getLength();
1096 for (sal_Int32 i = 0; i < n; ++i)
1098 const sal_Unicode c = p[i];
1099 if (c == '\\')
1101 aBuf.append(c);
1102 aBuf.append(c);
1104 else
1105 aBuf.append(c);
1107 return aBuf.makeStringAndClear();
1110 void XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
1112 const OUString& rFieldName = GetFieldName();
1113 if( rFieldName.isEmpty() )
1114 return;
1116 const XclImpPCField* pCacheField = GetCacheField();
1117 if( !pCacheField || !pCacheField->IsSupportedField() )
1118 return;
1120 ScDPSaveDimension* pTest = rSaveData.GetNewDimensionByName(rFieldName);
1121 if (!pTest)
1122 return;
1124 ScDPSaveDimension& rSaveDim = *pTest;
1126 // orientation
1127 rSaveDim.SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) );
1129 // visible name
1130 if (const OUString* pVisName = maFieldInfo.GetVisName())
1131 if (!pVisName->isEmpty())
1132 rSaveDim.SetLayoutName( *pVisName );
1134 // subtotal function(s)
1135 XclPTSubtotalVec aSubtotalVec;
1136 maFieldInfo.GetSubtotals( aSubtotalVec );
1137 if( !aSubtotalVec.empty() )
1138 rSaveDim.SetSubTotals( aSubtotalVec );
1140 // sorting
1141 DataPilotFieldSortInfo aSortInfo;
1142 aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
1143 aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
1144 aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
1145 rSaveDim.SetSortInfo( &aSortInfo );
1147 // auto show
1148 DataPilotFieldAutoShowInfo aShowInfo;
1149 aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
1150 aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
1151 aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
1152 aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
1153 rSaveDim.SetAutoShowInfo( &aShowInfo );
1155 // layout
1156 DataPilotFieldLayoutInfo aLayoutInfo;
1157 aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
1158 aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
1159 rSaveDim.SetLayoutInfo( &aLayoutInfo );
1161 // grouping info
1162 pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
1164 // custom subtotal name
1165 if (maFieldExtInfo.mpFieldTotalName)
1167 OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
1168 rSaveDim.SetSubtotalName(aSubName);
1172 void XclImpPTField::ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField ) const
1174 const OUString& rFieldName = GetFieldName();
1175 if( rFieldName.isEmpty() )
1176 return;
1178 const XclImpPCField* pCacheField = GetCacheField();
1179 if( !pCacheField || !pCacheField->IsSupportedField() )
1180 return;
1182 ScDPSaveDimension* pSaveDim = rSaveData.GetExistingDimensionByName(rFieldName);
1183 if (!pSaveDim)
1184 return;
1186 pSaveDim->SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
1187 for( const auto& rxItem : maItems )
1188 rxItem->ConvertItem( *pSaveDim, pObj, rRoot );
1190 if(bPageField && maPageInfo.mnSelItem != EXC_SXPI_ALLITEMS)
1192 const XclImpPTItem* pItem = GetItem( maPageInfo.mnSelItem );
1193 if(pItem)
1195 std::pair<bool, OUString> aReturnedName = pItem->GetItemName(*pSaveDim, pObj, rRoot);
1196 if(aReturnedName.first)
1197 pSaveDim->SetCurrentPage(&aReturnedName.second);
1202 void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
1204 // orientation
1205 rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
1206 // extended data field info
1207 ConvertDataFieldInfo( rSaveDim, rDataInfo );
1210 void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
1212 // visible name
1213 const OUString* pVisName = rDataInfo.GetVisName();
1214 if (pVisName && !pVisName->isEmpty())
1215 rSaveDim.SetLayoutName(*pVisName);
1217 // aggregation function
1218 rSaveDim.SetFunction( rDataInfo.GetApiAggFunc() );
1220 // result field reference
1221 sal_Int32 nRefType = rDataInfo.GetApiRefType();
1222 DataPilotFieldReference aFieldRef;
1223 aFieldRef.ReferenceType = nRefType;
1224 const XclImpPTField* pRefField = mrPTable.GetField(rDataInfo.mnRefField);
1225 if (pRefField)
1227 aFieldRef.ReferenceField = pRefField->GetFieldName();
1228 aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
1229 if (aFieldRef.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
1231 const OUString* pRefItemName = pRefField->GetItemName(rDataInfo.mnRefItem);
1232 if (pRefItemName)
1233 aFieldRef.ReferenceItemName = *pRefItemName;
1237 rSaveDim.SetReferenceValue(&aFieldRef);
1240 XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
1241 XclImpRoot( rRoot ),
1242 maDataOrientField( *this, EXC_SXIVD_DATA ),
1243 mpDPObj(nullptr)
1247 XclImpPivotTable::~XclImpPivotTable()
1251 // cache/field access, misc. --------------------------------------------------
1253 sal_uInt16 XclImpPivotTable::GetFieldCount() const
1255 return static_cast< sal_uInt16 >( maFields.size() );
1258 const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1260 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
1261 ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr);
1264 XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
1266 // do not return maDataOrientField
1267 return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
1270 const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
1272 if( nDataFieldIdx < maOrigDataFields.size() )
1273 return GetField( maOrigDataFields[ nDataFieldIdx ] );
1274 return nullptr;
1277 OUString XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
1279 if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
1280 return pField->GetFieldName();
1281 return OUString();
1284 // records --------------------------------------------------------------------
1286 void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
1288 rStrm >> maPTInfo;
1290 GetAddressConverter().ConvertRange(
1291 maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
1293 mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
1294 mxCurrField.reset();
1297 void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
1299 sal_uInt16 nFieldCount = GetFieldCount();
1300 if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
1302 // cache index for the field is equal to the SXVD record index
1303 mxCurrField.reset( new XclImpPTField( *this, nFieldCount ) );
1304 maFields.push_back( mxCurrField );
1305 mxCurrField->ReadSxvd( rStrm );
1306 // add visible name of new field to list of visible names
1307 maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
1308 OSL_ENSURE( maFields.size() == maVisFieldNames.size(),
1309 "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
1311 else
1312 mxCurrField.reset();
1315 void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
1317 if( mxCurrField )
1318 mxCurrField->ReadSxvi( rStrm );
1321 void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
1323 if( mxCurrField )
1324 mxCurrField->ReadSxvdex( rStrm );
1327 void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
1329 mxCurrField.reset();
1331 // find the index vector to fill (row SXIVD doesn't exist without row fields)
1332 ScfUInt16Vec* pFieldVec = nullptr;
1333 if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
1334 pFieldVec = &maRowFields;
1335 else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
1336 pFieldVec = &maColFields;
1338 // fill the vector from record data
1339 if( pFieldVec )
1341 sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
1342 pFieldVec->reserve( nSize );
1343 for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
1345 sal_uInt16 nFieldIdx;
1346 nFieldIdx = rStrm.ReaduInt16();
1347 pFieldVec->push_back( nFieldIdx );
1349 // set orientation at special data orientation field
1350 if( nFieldIdx == EXC_SXIVD_DATA )
1352 sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
1353 maDataOrientField.SetAxes( nAxis );
1359 void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
1361 mxCurrField.reset();
1363 sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
1364 for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
1366 XclPTPageFieldInfo aPageInfo;
1367 rStrm >> aPageInfo;
1368 if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
1370 maPageFields.push_back( aPageInfo.mnField );
1371 pField->SetPageFieldInfo( aPageInfo );
1373 GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
1377 void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
1379 mxCurrField.reset();
1381 XclPTDataFieldInfo aDataInfo;
1382 rStrm >> aDataInfo;
1383 if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
1385 maOrigDataFields.push_back( aDataInfo.mnField );
1386 // DataPilot does not support double data fields -> add first appearance to index list only
1387 if( !pField->HasDataFieldInfo() )
1388 maFiltDataFields.push_back( aDataInfo.mnField );
1389 pField->AddDataFieldInfo( aDataInfo );
1393 void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
1395 rStrm >> maPTExtInfo;
1398 void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
1400 rStrm >> maPTViewEx9Info;
1403 void XclImpPivotTable::ReadSxAddl( XclImpStream& rStrm )
1405 rStrm >> maPTAddlInfo;
1408 void XclImpPivotTable::Convert()
1410 if( !mxPCache || !mxPCache->IsValid() )
1411 return;
1413 if (utl::ConfigManager::IsFuzzing()) //just too slow
1414 return;
1416 ScDPSaveData aSaveData;
1418 // *** global settings ***
1420 aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
1421 aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
1422 aSaveData.SetFilterButton( false );
1423 aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
1424 aSaveData.SetIgnoreEmptyRows( false );
1425 aSaveData.SetRepeatIfEmpty( false );
1427 // *** fields ***
1429 // row fields
1430 for( const auto& rRowField : maRowFields )
1431 if( const XclImpPTField* pField = GetField( rRowField ) )
1432 pField->ConvertRowColField( aSaveData );
1434 // column fields
1435 for( const auto& rColField : maColFields )
1436 if( const XclImpPTField* pField = GetField( rColField ) )
1437 pField->ConvertRowColField( aSaveData );
1439 // page fields
1440 for( const auto& rPageField : maPageFields )
1441 if( const XclImpPTField* pField = GetField( rPageField ) )
1442 pField->ConvertPageField( aSaveData );
1444 // We need to import hidden fields because hidden fields may contain
1445 // special settings for subtotals (aggregation function, filters, custom
1446 // name etc.) and members (hidden, custom name etc.).
1448 // hidden fields
1449 for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
1450 if( const XclImpPTField* pField = GetField( nField ) )
1451 if (!pField->GetAxes())
1452 pField->ConvertHiddenField( aSaveData );
1454 // data fields
1455 for( const auto& rFiltDataField : maFiltDataFields )
1456 if( const XclImpPTField* pField = GetField( rFiltDataField ) )
1457 pField->ConvertDataField( aSaveData );
1459 // *** insert into Calc document ***
1461 // create source descriptor
1462 ScSheetSourceDesc aDesc(&GetDoc());
1463 const OUString& rSrcName = mxPCache->GetSourceRangeName();
1464 if (!rSrcName.isEmpty())
1465 // Range name is the data source.
1466 aDesc.SetRangeName(rSrcName);
1467 else
1468 // Normal cell range.
1469 aDesc.SetSourceRange(mxPCache->GetSourceRange());
1471 // adjust output range to include the page fields
1472 ScRange aOutRange( maOutScRange );
1473 if( !maPageFields.empty() )
1475 SCROW nDecRows = ::std::min< SCROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
1476 aOutRange.aStart.IncRow( -nDecRows );
1479 // create the DataPilot
1480 std::unique_ptr<ScDPObject> pDPObj(new ScDPObject( &GetDoc() ));
1481 pDPObj->SetName( maPTInfo.maTableName );
1482 if (!maPTInfo.maDataName.isEmpty())
1483 aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
1485 if (!maPTViewEx9Info.maGrandTotalName.isEmpty())
1486 aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
1488 pDPObj->SetSaveData( aSaveData );
1489 pDPObj->SetSheetDesc( aDesc );
1490 pDPObj->SetOutRange( aOutRange );
1491 pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
1493 mpDPObj = GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj));
1495 ApplyFieldInfo();
1496 ApplyMergeFlags(aOutRange, aSaveData);
1499 void XclImpPivotTable::MaybeRefresh()
1501 if (mpDPObj && mxPCache->IsRefreshOnLoad())
1503 // 'refresh table on load' flag is set. Refresh the table now. Some
1504 // Excel files contain partial table output when this flag is set.
1505 ScRange aOutRange = mpDPObj->GetOutRange();
1506 mpDPObj->Output(aOutRange.aStart);
1510 void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
1512 // Apply merge flags for various datapilot controls.
1514 ScDPOutputGeometry aGeometry(rOutRange, false);
1515 aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
1516 aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
1517 aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
1518 aGeometry.setRowFieldCount(maPTInfo.mnRowFields);
1520 // Make sure we set headerlayout when input file has additional raw header
1521 if(maPTInfo.mnColFields == 0)
1523 mpDPObj->SetHeaderLayout( maPTInfo.mnFirstHeadRow - 2 == static_cast<sal_uInt16>(aGeometry.getRowFieldHeaderRow()) );
1525 aGeometry.setHeaderLayout(mpDPObj->GetHeaderLayout());
1526 aGeometry.setCompactMode(maPTAddlInfo.mbCompactMode);
1528 ScDocument& rDoc = GetDoc();
1530 vector<const ScDPSaveDimension*> aFieldDims;
1531 vector<ScAddress> aFieldBtns;
1533 aGeometry.getPageFieldPositions(aFieldBtns);
1534 for (const auto& rFieldBtn : aFieldBtns)
1536 rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), ScMF::Button);
1538 ScMF nMFlag = ScMF::ButtonPopup;
1539 OUString aName = rDoc.GetString(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab());
1540 if (rSaveData.HasInvisibleMember(aName))
1541 nMFlag |= ScMF::HiddenMember;
1543 rDoc.ApplyFlagsTab(rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
1546 aGeometry.getColumnFieldPositions(aFieldBtns);
1547 rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aFieldDims);
1548 if (aFieldBtns.size() == aFieldDims.size())
1550 vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
1551 for (const auto& rFieldBtn : aFieldBtns)
1553 ScMF nMFlag = ScMF::Button;
1554 const ScDPSaveDimension* pDim = *itDim;
1555 if (pDim->HasInvisibleMember())
1556 nMFlag |= ScMF::HiddenMember;
1557 if (!pDim->IsDataLayout())
1558 nMFlag |= ScMF::ButtonPopup;
1559 rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
1560 ++itDim;
1564 aGeometry.getRowFieldPositions(aFieldBtns);
1565 rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aFieldDims);
1566 if ((aFieldBtns.size() == aFieldDims.size()) || (maPTAddlInfo.mbCompactMode && aFieldBtns.size() == 1))
1568 vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
1569 for (const auto& rFieldBtn : aFieldBtns)
1571 ScMF nMFlag = ScMF::Button;
1572 const ScDPSaveDimension* pDim = itDim != aFieldDims.end() ? *itDim++ : nullptr;
1573 if (pDim && pDim->HasInvisibleMember())
1574 nMFlag |= ScMF::HiddenMember;
1575 if (!pDim || !pDim->IsDataLayout())
1576 nMFlag |= ScMF::ButtonPopup;
1577 rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
1583 void XclImpPivotTable::ApplyFieldInfo()
1585 mpDPObj->BuildAllDimensionMembers();
1586 ScDPSaveData& rSaveData = *mpDPObj->GetSaveData();
1588 // row fields
1589 for( const auto& rRowField : maRowFields )
1590 if( const XclImpPTField* pField = GetField( rRowField ) )
1591 pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
1593 // column fields
1594 for( const auto& rColField : maColFields )
1595 if( const XclImpPTField* pField = GetField( rColField ) )
1596 pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
1598 // page fields
1599 for( const auto& rPageField : maPageFields )
1600 if( const XclImpPTField* pField = GetField( rPageField ) )
1601 pField->ConvertFieldInfo( rSaveData, mpDPObj, *this, true );
1603 // hidden fields
1604 for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
1605 if( const XclImpPTField* pField = GetField( nField ) )
1606 if (!pField->GetAxes())
1607 pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
1610 XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
1611 XclImpRoot( rRoot )
1615 XclImpPivotTableManager::~XclImpPivotTableManager()
1619 // pivot cache records --------------------------------------------------------
1621 XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
1623 XclImpPivotCacheRef xPCache;
1624 if( nCacheIdx < maPCaches.size() )
1625 xPCache = maPCaches[ nCacheIdx ];
1626 return xPCache;
1629 void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
1631 XclImpPivotCacheRef xPCache( new XclImpPivotCache( GetRoot() ) );
1632 maPCaches.push_back( xPCache );
1633 xPCache->ReadSxidstm( rStrm );
1636 void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
1638 if( !maPCaches.empty() )
1639 maPCaches.back()->ReadSxvs( rStrm );
1642 void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
1644 if( !maPCaches.empty() )
1645 maPCaches.back()->ReadDconref( rStrm );
1648 void XclImpPivotTableManager::ReadDConName( XclImpStream& rStrm )
1650 if( !maPCaches.empty() )
1651 maPCaches.back()->ReadDConName( rStrm );
1654 // pivot table records --------------------------------------------------------
1656 void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
1658 XclImpPivotTableRef xPTable( new XclImpPivotTable( GetRoot() ) );
1659 maPTables.push_back( xPTable );
1660 xPTable->ReadSxview( rStrm );
1663 void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
1665 if( !maPTables.empty() )
1666 maPTables.back()->ReadSxvd( rStrm );
1669 void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
1671 if( !maPTables.empty() )
1672 maPTables.back()->ReadSxvdex( rStrm );
1675 void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
1677 if( !maPTables.empty() )
1678 maPTables.back()->ReadSxivd( rStrm );
1681 void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
1683 if( !maPTables.empty() )
1684 maPTables.back()->ReadSxpi( rStrm );
1687 void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
1689 if( !maPTables.empty() )
1690 maPTables.back()->ReadSxdi( rStrm );
1693 void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
1695 if( !maPTables.empty() )
1696 maPTables.back()->ReadSxvi( rStrm );
1699 void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
1701 if( !maPTables.empty() )
1702 maPTables.back()->ReadSxex( rStrm );
1705 void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
1707 if( !maPTables.empty() )
1708 maPTables.back()->ReadSxViewEx9( rStrm );
1711 void XclImpPivotTableManager::ReadSxAddl( XclImpStream& rStrm )
1713 if( !maPTables.empty() )
1714 maPTables.back()->ReadSxAddl( rStrm );
1717 void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream& rStrm )
1719 for( auto& rxPCache : maPCaches )
1720 rxPCache->ReadPivotCacheStream( rStrm );
1723 void XclImpPivotTableManager::ConvertPivotTables()
1725 for( auto& rxPTable : maPTables )
1726 rxPTable->Convert();
1729 void XclImpPivotTableManager::MaybeRefreshPivotTables()
1731 for( auto& rxPTable : maPTables )
1732 rxPTable->MaybeRefresh();
1735 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */