Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / filter / excel / xipivot.cxx
blob11fe5a15bde42444978166bf26a6637d4a3577f0
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>
27 #include <tools/datetime.hxx>
28 #include <svl/zformat.hxx>
29 #include <svl/intitem.hxx>
31 #include "document.hxx"
32 #include "formulacell.hxx"
33 #include "dpsave.hxx"
34 #include "dpdimsave.hxx"
35 #include "dpobject.hxx"
36 #include "dpshttab.hxx"
37 #include "dpoutputgeometry.hxx"
38 #include "scitems.hxx"
39 #include "attrib.hxx"
41 #include "xltracer.hxx"
42 #include "xistream.hxx"
43 #include "xihelper.hxx"
44 #include "xilink.hxx"
45 #include "xiescher.hxx"
47 //TODO ExcelToSc usage
48 #include "excform.hxx"
49 #include "xltable.hxx"
50 #include "documentimport.hxx"
52 #include <vector>
54 using namespace com::sun::star;
56 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
57 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
58 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
59 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
60 using ::com::sun::star::sheet::DataPilotFieldReference;
61 using ::std::vector;
63 // Pivot cache
65 XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
67 switch( rStrm.GetRecId() )
69 case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
70 case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
71 case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
72 case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
73 case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
74 case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
75 case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
76 default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
80 namespace {
82 void lclSetValue( XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, short nFormatType )
84 ScDocumentImport& rDoc = rRoot.GetDocImport();
85 rDoc.setNumericCell(rScPos, fValue);
86 sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
87 rDoc.getDoc().ApplyAttr(
88 rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT, nScNumFmt));
91 } // namespace
93 void XclImpPCItem::WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const
95 ScDocumentImport& rDoc = rRoot.GetDocImport();
96 if( const OUString* pText = GetText() )
97 rDoc.setStringCell(rScPos, *pText);
98 else if( const double* pfValue = GetDouble() )
99 rDoc.setNumericCell(rScPos, *pfValue);
100 else if( const sal_Int16* pnValue = GetInteger() )
101 rDoc.setNumericCell(rScPos, *pnValue);
102 else if( const bool* pbValue = GetBool() )
103 lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, css::util::NumberFormat::LOGICAL );
104 else if( const DateTime* pDateTime = GetDateTime() )
106 // set number format date, time, or date/time, depending on the value
107 double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
108 double fInt = 0.0;
109 double fFrac = modf( fValue, &fInt );
110 short nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? css::util::NumberFormat::DATE :
111 ((fInt == 0.0) ? css::util::NumberFormat::TIME : css::util::NumberFormat::DATETIME);
112 lclSetValue( rRoot, rScPos, fValue, nFormatType );
114 else if( const sal_uInt16* pnError = GetError() )
116 double fValue;
117 sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
118 const ScTokenArray* pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
119 XclTools::ErrorToEnum( fValue, true, nErrCode ) );
120 ScFormulaCell* pCell = pScTokArr ? new ScFormulaCell(&rDoc.getDoc(), rScPos, *pScTokArr) : new ScFormulaCell(&rDoc.getDoc(), rScPos);
121 pCell->SetHybridDouble( fValue );
122 rDoc.setFormulaCell(rScPos, pCell);
126 void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
128 OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
129 SetDouble( rStrm.ReadDouble() );
132 void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
134 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
135 SetBool( rStrm.ReaduInt16() != 0 );
138 void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
140 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
141 SetError( rStrm.ReaduInt16() );
144 void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
146 OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
147 SetInteger( rStrm.ReadInt16() );
150 void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
152 OSL_ENSURE( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
153 SetText( rStrm.ReadUniString() );
156 void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
158 OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
159 sal_uInt16 nYear, nMonth;
160 sal_uInt8 nDay, nHour, nMin, nSec;
161 nYear = rStrm.ReaduInt16();
162 nMonth = rStrm.ReaduInt16();
163 nDay = rStrm.ReaduInt8();
164 nHour = rStrm.ReaduInt8();
165 nMin = rStrm.ReaduInt8();
166 nSec = rStrm.ReaduInt8();
167 SetDateTime( DateTime( Date( nDay, nMonth, nYear ), tools::Time( nHour, nMin, nSec ) ) );
170 void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
172 (void)rStrm; // avoid compiler warning
173 OSL_ENSURE( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
174 SetEmpty();
177 XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
178 XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
179 XclImpRoot( rRoot ),
180 mrPCache( rPCache ),
181 mnSourceScCol( -1 ),
182 mbNumGroupInfoRead( false )
186 XclImpPCField::~XclImpPCField()
190 // general field/item access --------------------------------------------------
192 const OUString& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
194 if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
196 const OUString& rVisName = rVisNames[ mnFieldIdx ];
197 if (!rVisName.isEmpty())
198 return rVisName;
200 return maFieldInfo.maName;
203 const XclImpPCField* XclImpPCField::GetGroupBaseField() const
205 OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
206 return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : nullptr;
209 const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
211 return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
214 const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
216 OSL_ENSURE( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
217 OSL_ENSURE( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
218 return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : nullptr;
221 void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab )
223 OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
224 GetDocImport().setStringCell(ScAddress(nScCol, 0, nScTab), maFieldInfo.maName);
225 mnSourceScCol = nScCol;
228 void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx )
230 if( nItemIdx < maOrigItems.size() )
231 maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
234 void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab )
236 if( !maOrigItems.empty() )
237 maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
240 // records --------------------------------------------------------------------
242 void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
244 rStrm >> maFieldInfo;
246 /* Detect the type of this field. This is done very restrictive to detect
247 any unexpected state. */
248 meFieldType = EXC_PCFIELD_UNKNOWN;
250 bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
251 bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
252 bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
253 bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
254 bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
256 sal_uInt16 nVisC = maFieldInfo.mnVisItems;
257 sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
258 sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
259 sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
260 OSL_ENSURE( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
262 sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
263 bool bType =
264 (nType == EXC_SXFIELD_DATA_STR) ||
265 (nType == EXC_SXFIELD_DATA_INT) ||
266 (nType == EXC_SXFIELD_DATA_DBL) ||
267 (nType == EXC_SXFIELD_DATA_STR_INT) ||
268 (nType == EXC_SXFIELD_DATA_STR_DBL) ||
269 (nType == EXC_SXFIELD_DATA_DATE) ||
270 (nType == EXC_SXFIELD_DATA_DATE_EMP) ||
271 (nType == EXC_SXFIELD_DATA_DATE_NUM) ||
272 (nType == EXC_SXFIELD_DATA_DATE_STR);
273 bool bTypeNone =
274 (nType == EXC_SXFIELD_DATA_NONE);
275 // for now, ignore data type of calculated fields
276 OSL_ENSURE( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
278 if( nVisC > 0 || bPostp )
280 if( bItems && !bPostp )
282 if( !bCalced )
284 // 1) standard fields and standard grouping fields
285 if( !bNum )
287 // 1a) standard field without grouping
288 if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
289 meFieldType = EXC_PCFIELD_STANDARD;
291 // 1b) standard grouping field
292 else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
293 meFieldType = EXC_PCFIELD_STDGROUP;
295 // 2) numerical grouping fields
296 else if( (nGroupC == nVisC) && (nBaseC == 0) )
298 // 2a) single num/date grouping field without child grouping field
299 if( !bChild && bType && (nOrigC > 0) )
301 switch( nType )
303 case EXC_SXFIELD_DATA_INT:
304 case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
305 case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
306 default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
310 // 2b) first date grouping field with child grouping field
311 else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
312 meFieldType = EXC_PCFIELD_DATEGROUP;
314 // 2c) additional date grouping field
315 else if( bTypeNone && (nOrigC == 0) )
316 meFieldType = EXC_PCFIELD_DATECHILD;
318 OSL_ENSURE( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
321 // 3) calculated field
322 else
324 if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
325 meFieldType = EXC_PCFIELD_CALCED;
326 OSL_ENSURE( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
330 else if( !bItems && bPostp )
332 // 4) standard field with postponed items
333 if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
334 meFieldType = EXC_PCFIELD_STANDARD;
335 OSL_ENSURE( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
340 void XclImpPCField::ReadItem( XclImpStream& rStrm )
342 OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
344 // read the item
345 XclImpPCItemRef xItem( new XclImpPCItem( rStrm ) );
347 // try to insert into an item list
348 if( mbNumGroupInfoRead )
350 // there are 3 items after SXNUMGROUP that contain grouping limits and step count
351 if( maNumGroupItems.size() < 3 )
352 maNumGroupItems.push_back( xItem );
353 else
354 maOrigItems.push_back( xItem );
356 else if( HasInlineItems() || HasPostponedItems() )
358 maItems.push_back( xItem );
359 // visible item is original item in standard fields
360 if( IsStandardField() )
361 maOrigItems.push_back( xItem );
365 void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
367 OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
368 OSL_ENSURE( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
369 OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
370 rStrm >> maNumGroupInfo;
371 mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
374 void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
376 OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
377 OSL_ENSURE( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
378 OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
379 OSL_ENSURE( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
380 maGroupOrder.clear();
381 size_t nSize = rStrm.GetRecLeft() / 2;
382 maGroupOrder.resize( nSize, 0 );
383 for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
384 maGroupOrder[ nIdx ] = rStrm.ReaduInt16();
387 // grouping -------------------------------------------------------------------
389 void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
391 if (!GetFieldName(rVisNames).isEmpty())
393 if( IsStdGroupField() )
394 ConvertStdGroupField( rSaveData, rVisNames );
395 else if( IsNumGroupField() )
396 ConvertNumGroupField( rSaveData, rVisNames );
397 else if( IsDateGroupField() )
398 ConvertDateGroupField( rSaveData, rVisNames );
402 // private --------------------------------------------------------------------
404 void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
406 if( const XclImpPCField* pBaseField = GetGroupBaseField() )
408 const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
409 if( !rBaseFieldName.isEmpty() )
411 // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
412 ScDPSaveGroupItemVec aGroupItems;
413 aGroupItems.reserve( maItems.size() );
414 // initialize with own item names
415 for( XclImpPCItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt )
416 aGroupItems.push_back( ScDPSaveGroupItem( (*aIt)->ConvertToText() ) );
418 // *** iterate over all base items, set their names at corresponding own items ***
419 for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
420 if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
421 if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
422 if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
423 if( *pBaseItem != *pGroupItem )
424 aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
426 // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
427 ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
428 for( ScDPSaveGroupItemVec::const_iterator aIt = aGroupItems.begin(), aEnd = aGroupItems.end(); aIt != aEnd; ++aIt )
429 if( !aIt->IsEmpty() )
430 aGroupDim.AddGroupItem( *aIt );
431 rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
436 void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
438 ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
439 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
440 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
443 void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
445 ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
446 sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
448 switch( meFieldType )
450 case EXC_PCFIELD_DATEGROUP:
452 if( aDateInfo.mbDateValues )
454 // special case for days only with step value - create numeric grouping
455 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
456 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
458 else
460 ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
461 aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
462 rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
465 break;
467 case EXC_PCFIELD_DATECHILD:
469 if( const XclImpPCField* pBaseField = GetGroupBaseField() )
471 const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
472 if( !rBaseFieldName.isEmpty() )
474 ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
475 aGroupDim.SetDateInfo( aDateInfo, nScDateType );
476 rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
480 break;
482 default:
483 OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
487 ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
489 ScDPNumGroupInfo aNumInfo;
490 aNumInfo.mbEnable = true;
491 aNumInfo.mbDateValues = false;
492 aNumInfo.mbAutoStart = true;
493 aNumInfo.mbAutoEnd = true;
495 if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
497 aNumInfo.mfStart = *pfMinValue;
498 aNumInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
500 if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
502 aNumInfo.mfEnd = *pfMaxValue;
503 aNumInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
505 if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
506 aNumInfo.mfStep = *pfStepValue;
508 return aNumInfo;
511 ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
513 ScDPNumGroupInfo aDateInfo;
514 aDateInfo.mbEnable = true;
515 aDateInfo.mbDateValues = false;
516 aDateInfo.mbAutoStart = true;
517 aDateInfo.mbAutoEnd = true;
519 if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
521 aDateInfo.mfStart = GetDoubleFromDateTime( *pMinDate );
522 aDateInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
524 if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
526 aDateInfo.mfEnd = GetDoubleFromDateTime( *pMaxDate );
527 aDateInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
529 // GetDateGroupStep() returns a value for date type "day" in single date groups only
530 if( const sal_Int16* pnStepValue = GetDateGroupStep() )
532 aDateInfo.mfStep = *pnStepValue;
533 aDateInfo.mbDateValues = true;
536 return aDateInfo;
539 const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
541 OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
542 if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
544 OSL_ENSURE( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
545 return pItem->GetDouble();
547 return nullptr;
550 const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
552 OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
553 if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
555 OSL_ENSURE( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
556 return pItem->GetDateTime();
558 return nullptr;
561 const sal_Int16* XclImpPCField::GetDateGroupStep() const
563 // only for single date grouping fields, not for grouping chains
564 if( !IsGroupBaseField() && !IsGroupChildField() )
566 // only days may have a step value, return 0 for all other date types
567 if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
569 if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
571 OSL_ENSURE( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
572 if( const sal_Int16* pnStep = pItem->GetInteger() )
574 OSL_ENSURE( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
575 // return nothing for step count 1 - this is also a standard date group in Excel
576 return (*pnStep > 1) ? pnStep : nullptr;
581 return nullptr;
584 XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
585 XclImpRoot( rRoot ),
586 maSrcRange( ScAddress::INITIALIZE_INVALID ),
587 mnStrmId( 0 ),
588 mnSrcType( EXC_SXVS_UNKNOWN ),
589 mbSelfRef( false )
593 XclImpPivotCache::~XclImpPivotCache()
597 // data access ----------------------------------------------------------------
599 sal_uInt16 XclImpPivotCache::GetFieldCount() const
601 return static_cast< sal_uInt16 >( maFields.size() );
604 const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
606 return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
609 // records --------------------------------------------------------------------
611 void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
613 mnStrmId = rStrm.ReaduInt16();
616 void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
618 mnSrcType = rStrm.ReaduInt16();
619 GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
622 void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
624 /* Read DCONREF only once (by checking maTabName), there may be other
625 DCONREF records in another context. Read reference only if a leading
626 SXVS record is present (by checking mnSrcType). */
627 if( !maTabName.isEmpty() || (mnSrcType != EXC_SXVS_SHEET) )
628 return;
630 XclRange aXclRange( ScAddress::UNINITIALIZED );
631 aXclRange.Read( rStrm, false );
632 OUString aEncUrl = rStrm.ReadUniString();
634 XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
636 /* Do not convert maTabName to Calc sheet name -> original name is used to
637 find the sheet in the document. Sheet index of source range will be
638 found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
639 may not exist yet. */
640 if( mbSelfRef )
641 GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
644 void XclImpPivotCache::ReadDConName( XclImpStream& rStrm )
646 maSrcRangeName = rStrm.ReadUniString();
648 // This 2-byte value equals the length of string that follows, or if 0 it
649 // indicates that the name has a workbook scope. For now, we only support
650 // internal defined name with a workbook scope.
651 sal_uInt16 nFlag;
652 nFlag = rStrm.ReaduInt16();
653 mbSelfRef = (nFlag == 0);
655 if (!mbSelfRef)
656 // External name is not supported yet.
657 maSrcRangeName.clear();
660 void XclImpPivotCache::ReadPivotCacheStream( XclImpStream& rStrm )
662 if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
663 return;
665 ScDocument& rDoc = GetDoc();
666 SCCOL nFieldScCol = 0; // column index of source data for next field
667 SCROW nItemScRow = 0; // row index of source data for current items
668 SCTAB nScTab = 0; // sheet index of source data
669 bool bGenerateSource = false; // true = write source data from cache to dummy table
671 if( mbSelfRef )
673 if (maSrcRangeName.isEmpty())
675 // try to find internal sheet containing the source data
676 nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
677 if( rDoc.HasTable( nScTab ) )
679 // set sheet index to source range
680 maSrcRange.aStart.SetTab( nScTab );
681 maSrcRange.aEnd.SetTab( nScTab );
683 else
685 // create dummy sheet for deleted internal sheet
686 bGenerateSource = true;
690 else
692 // create dummy sheet for external sheet
693 bGenerateSource = true;
696 // create dummy sheet for source data from external or deleted sheet
697 if( bGenerateSource )
699 if( rDoc.GetTableCount() >= MAXTABCOUNT )
700 // cannot create more sheets -> exit
701 return;
703 nScTab = rDoc.GetTableCount();
704 rDoc.MakeTable( nScTab );
705 OUStringBuffer aDummyName("DPCache");
706 if( maTabName.getLength() > 0 )
707 aDummyName.append( '_' ).append( maTabName );
708 OUString aName = aDummyName.makeStringAndClear();
709 rDoc.CreateValidTabName( aName );
710 rDoc.RenameTab( nScTab, aName );
711 // set sheet index to source range
712 maSrcRange.aStart.SetTab( nScTab );
713 maSrcRange.aEnd.SetTab( nScTab );
716 // open pivot cache storage stream
717 tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
718 tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
719 if( !xSvStrm.Is() )
720 return;
722 // create Excel record stream object
723 XclImpStream aPCStrm( *xSvStrm, GetRoot() );
724 aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
726 XclImpPCFieldRef xCurrField; // current field for new items
727 XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
728 XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
729 size_t nPostpIdx = 0; // index to current field with postponed items
730 bool bLoop = true; // true = continue loop
732 while( bLoop && aPCStrm.StartNextRecord() )
734 switch( aPCStrm.GetRecId() )
736 case EXC_ID_EOF:
737 bLoop = false;
738 break;
740 case EXC_ID_SXDB:
741 aPCStrm >> maPCInfo;
742 break;
744 case EXC_ID_SXFIELD:
746 xCurrField.reset();
747 sal_uInt16 nNewFieldIdx = GetFieldCount();
748 if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
750 xCurrField.reset( new XclImpPCField( GetRoot(), *this, nNewFieldIdx ) );
751 maFields.push_back( xCurrField );
752 xCurrField->ReadSxfield( aPCStrm );
753 if( xCurrField->HasOrigItems() )
755 if( xCurrField->HasPostponedItems() )
756 aPostpFields.push_back( xCurrField );
757 else
758 aOrigFields.push_back( xCurrField );
759 // insert field name into generated source data, field remembers its column index
760 if( bGenerateSource && (nFieldScCol <= MAXCOL) )
761 xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
763 // do not read items into invalid/postponed fields
764 if( !xCurrField->HasInlineItems() )
765 xCurrField.reset();
768 break;
770 case EXC_ID_SXINDEXLIST:
771 // read index list and insert all items into generated source data
772 if( bGenerateSource && (nItemScRow <= MAXROW) && (++nItemScRow <= MAXROW) )
774 for( XclImpPCFieldVec::const_iterator aIt = aOrigFields.begin(), aEnd = aOrigFields.end(); aIt != aEnd; ++aIt )
776 sal_uInt16 nItemIdx = (*aIt)->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
777 (*aIt)->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
780 xCurrField.reset();
781 break;
783 case EXC_ID_SXDOUBLE:
784 case EXC_ID_SXBOOLEAN:
785 case EXC_ID_SXERROR:
786 case EXC_ID_SXINTEGER:
787 case EXC_ID_SXSTRING:
788 case EXC_ID_SXDATETIME:
789 case EXC_ID_SXEMPTY:
790 if( xCurrField ) // inline items
792 xCurrField->ReadItem( aPCStrm );
794 else if( !aPostpFields.empty() ) // postponed items
796 // read postponed item
797 aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
798 // write item to source
799 if( bGenerateSource && (nItemScRow <= MAXROW) )
801 // start new row, if there are only postponed fields
802 if( aOrigFields.empty() && (nPostpIdx == 0) )
803 ++nItemScRow;
804 if( nItemScRow <= MAXROW )
805 aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
807 // get index of next postponed field
808 ++nPostpIdx;
809 if( nPostpIdx >= aPostpFields.size() )
810 nPostpIdx = 0;
812 break;
814 case EXC_ID_SXNUMGROUP:
815 if( xCurrField )
816 xCurrField->ReadSxnumgroup( aPCStrm );
817 break;
819 case EXC_ID_SXGROUPINFO:
820 if( xCurrField )
821 xCurrField->ReadSxgroupinfo( aPCStrm );
822 break;
824 // known but ignored records
825 case EXC_ID_SXRULE:
826 case EXC_ID_SXFILT:
827 case EXC_ID_00F5:
828 case EXC_ID_SXNAME:
829 case EXC_ID_SXPAIR:
830 case EXC_ID_SXFMLA:
831 case EXC_ID_SXFORMULA:
832 case EXC_ID_SXDBEX:
833 case EXC_ID_SXFDBTYPE:
834 break;
836 default:
837 OSL_TRACE( "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x%04hX", aPCStrm.GetRecId() );
841 OSL_ENSURE( maPCInfo.mnTotalFields == maFields.size(),
842 "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
844 if (HasCacheRecords())
846 SCROW nNewEnd = maSrcRange.aStart.Row() + maPCInfo.mnSrcRecs;
847 maSrcRange.aEnd.SetRow(nNewEnd);
850 // set source range for external source data
851 if( bGenerateSource && (nFieldScCol > 0) )
853 maSrcRange.aStart.SetCol( 0 );
854 maSrcRange.aStart.SetRow( 0 );
855 // nFieldScCol points to first unused column
856 maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
857 // nItemScRow points to last used row
858 maSrcRange.aEnd.SetRow( nItemScRow );
862 bool XclImpPivotCache::HasCacheRecords() const
864 return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_SAVEDATA);
867 bool XclImpPivotCache::IsRefreshOnLoad() const
869 return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_REFRESH_LOAD);
872 bool XclImpPivotCache::IsValid() const
874 if (!maSrcRangeName.isEmpty())
875 return true;
877 return maSrcRange.IsValid();
880 // Pivot table
882 XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) :
883 mpCacheField( pCacheField )
887 const OUString* XclImpPTItem::GetItemName() const
889 if( mpCacheField )
890 if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
891 //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
892 return pCacheItem->IsEmpty() ? nullptr : pCacheItem->GetText();
893 return nullptr;
896 void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
898 rStrm >> maItemInfo;
901 void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim ) const
903 if (const OUString* pItemName = GetItemName())
905 ScDPSaveMember& rMember = *rSaveDim.GetMemberByName( *pItemName );
906 rMember.SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
907 rMember.SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
908 if (maItemInfo.HasVisName())
909 rMember.SetLayoutName(*maItemInfo.GetVisName());
913 XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
914 mrPTable( rPTable )
916 maFieldInfo.mnCacheIdx = nCacheIdx;
919 // general field/item access --------------------------------------------------
921 const XclImpPCField* XclImpPTField::GetCacheField() const
923 XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
924 return xPCache ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : nullptr;
927 OUString XclImpPTField::GetFieldName() const
929 const XclImpPCField* pField = GetCacheField();
930 return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : OUString();
933 OUString XclImpPTField::GetVisFieldName() const
935 const OUString* pVisName = maFieldInfo.GetVisName();
936 return pVisName ? *pVisName : OUString();
939 const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
941 return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
944 const OUString* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
946 const XclImpPTItem* pItem = GetItem( nItemIdx );
947 return pItem ? pItem->GetItemName() : nullptr;
950 // records --------------------------------------------------------------------
952 void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
954 rStrm >> maFieldInfo;
957 void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
959 rStrm >> maFieldExtInfo;
962 void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
964 XclImpPTItemRef xItem( new XclImpPTItem( GetCacheField() ) );
965 maItems.push_back( xItem );
966 xItem->ReadSxvi( rStrm );
969 // row/column fields ----------------------------------------------------------
971 void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
973 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
974 // special data orientation field?
975 if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
976 rSaveData.GetDataLayoutDimension()->SetOrientation( static_cast< sal_uInt16 >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) ) );
977 else
978 ConvertRCPField( rSaveData );
981 // page fields ----------------------------------------------------------------
983 void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
985 maPageInfo = rPageInfo;
988 void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
990 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
991 if( ScDPSaveDimension* pSaveDim = ConvertRCPField( rSaveData ) )
993 const OUString* pName = GetItemName( maPageInfo.mnSelItem );
994 if (pName)
995 pSaveDim->SetCurrentPage(pName);
999 // hidden fields --------------------------------------------------------------
1001 void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
1003 OSL_ENSURE( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
1004 ConvertRCPField( rSaveData );
1007 // data fields ----------------------------------------------------------------
1009 bool XclImpPTField::HasDataFieldInfo() const
1011 return !maDataInfoList.empty();
1014 void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
1016 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
1017 maDataInfoList.push_back( rDataInfo );
1020 void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
1022 OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
1023 OSL_ENSURE( !maDataInfoList.empty(), "XclImpPTField::ConvertDataField - no data field info" );
1024 if (maDataInfoList.empty())
1025 return;
1027 OUString aFieldName = GetFieldName();
1028 if (aFieldName.isEmpty())
1029 return;
1031 ScDPSaveDimension* pSaveDim = rSaveData.GetNewDimensionByName(aFieldName);
1032 if (!pSaveDim)
1034 SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName);
1035 return;
1038 XclPTDataFieldInfoList::const_iterator aIt = maDataInfoList.begin(), aEnd = maDataInfoList.end();
1040 ConvertDataField( *pSaveDim, *aIt );
1042 // multiple data fields -> clone dimension
1043 for( ++aIt; aIt != aEnd; ++aIt )
1045 ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( *pSaveDim );
1046 ConvertDataFieldInfo( rDupDim, *aIt );
1050 // private --------------------------------------------------------------------
1053 * Convert Excel-encoded subtotal name to a Calc-encoded one.
1055 static OUString lcl_convertExcelSubtotalName(const OUString& rName)
1057 OUStringBuffer aBuf;
1058 const sal_Unicode* p = rName.getStr();
1059 sal_Int32 n = rName.getLength();
1060 for (sal_Int32 i = 0; i < n; ++i)
1062 const sal_Unicode c = p[i];
1063 if (c == '\\')
1065 aBuf.append(c);
1066 aBuf.append(c);
1068 else
1069 aBuf.append(c);
1071 return aBuf.makeStringAndClear();
1074 ScDPSaveDimension* XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
1076 const OUString& rFieldName = GetFieldName();
1077 if( rFieldName.isEmpty() )
1078 return nullptr;
1080 const XclImpPCField* pCacheField = GetCacheField();
1081 if( !pCacheField || !pCacheField->IsSupportedField() )
1082 return nullptr;
1084 ScDPSaveDimension* pTest = rSaveData.GetNewDimensionByName(rFieldName);
1085 if (!pTest)
1086 return nullptr;
1088 ScDPSaveDimension& rSaveDim = *pTest;
1090 // orientation
1091 rSaveDim.SetOrientation( static_cast< sal_uInt16 >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) ) );
1093 // general field info
1094 ConvertFieldInfo( rSaveDim );
1096 // visible name
1097 if (const OUString* pVisName = maFieldInfo.GetVisName())
1098 if (!pVisName->isEmpty())
1099 rSaveDim.SetLayoutName( *pVisName );
1101 // subtotal function(s)
1102 XclPTSubtotalVec aSubtotalVec;
1103 maFieldInfo.GetSubtotals( aSubtotalVec );
1104 if( !aSubtotalVec.empty() )
1105 rSaveDim.SetSubTotals( static_cast< long >( aSubtotalVec.size() ), &aSubtotalVec[ 0 ] );
1107 // sorting
1108 DataPilotFieldSortInfo aSortInfo;
1109 aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
1110 aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
1111 aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
1112 rSaveDim.SetSortInfo( &aSortInfo );
1114 // auto show
1115 DataPilotFieldAutoShowInfo aShowInfo;
1116 aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
1117 aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
1118 aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
1119 aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
1120 rSaveDim.SetAutoShowInfo( &aShowInfo );
1122 // layout
1123 DataPilotFieldLayoutInfo aLayoutInfo;
1124 aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
1125 aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
1126 rSaveDim.SetLayoutInfo( &aLayoutInfo );
1128 // grouping info
1129 pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
1131 // custom subtotal name
1132 if (maFieldExtInfo.mpFieldTotalName.get())
1134 OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
1135 rSaveDim.SetSubtotalName(aSubName);
1138 return &rSaveDim;
1141 void XclImpPTField::ConvertFieldInfo( ScDPSaveDimension& rSaveDim ) const
1143 rSaveDim.SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
1144 ConvertItems( rSaveDim );
1147 void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
1149 // orientation
1150 rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
1151 // general field info
1152 ConvertFieldInfo( rSaveDim );
1153 // extended data field info
1154 ConvertDataFieldInfo( rSaveDim, rDataInfo );
1157 void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
1159 // visible name
1160 const OUString* pVisName = rDataInfo.GetVisName();
1161 if (pVisName && !pVisName->isEmpty())
1162 rSaveDim.SetLayoutName(*pVisName);
1164 // aggregation function
1165 rSaveDim.SetFunction( static_cast< sal_uInt16 >( rDataInfo.GetApiAggFunc() ) );
1167 // result field reference
1168 sal_Int32 nRefType = rDataInfo.GetApiRefType();
1169 DataPilotFieldReference aFieldRef;
1170 aFieldRef.ReferenceType = nRefType;
1171 const XclImpPTField* pRefField = mrPTable.GetField(rDataInfo.mnRefField);
1172 if (pRefField)
1174 aFieldRef.ReferenceField = pRefField->GetFieldName();
1175 aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
1176 if (aFieldRef.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
1178 const OUString* pRefItemName = pRefField->GetItemName(rDataInfo.mnRefItem);
1179 if (pRefItemName)
1180 aFieldRef.ReferenceItemName = *pRefItemName;
1184 rSaveDim.SetReferenceValue(&aFieldRef);
1187 void XclImpPTField::ConvertItems( ScDPSaveDimension& rSaveDim ) const
1189 for( XclImpPTItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt )
1190 (*aIt)->ConvertItem( rSaveDim );
1193 XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
1194 XclImpRoot( rRoot ),
1195 maDataOrientField( *this, EXC_SXIVD_DATA ),
1196 mpDPObj(nullptr)
1200 XclImpPivotTable::~XclImpPivotTable()
1204 // cache/field access, misc. --------------------------------------------------
1206 sal_uInt16 XclImpPivotTable::GetFieldCount() const
1208 return static_cast< sal_uInt16 >( maFields.size() );
1211 const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1213 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
1214 ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr);
1217 XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
1219 // do not return maDataOrientField
1220 return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
1223 const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
1225 if( nDataFieldIdx < maOrigDataFields.size() )
1226 return GetField( maOrigDataFields[ nDataFieldIdx ] );
1227 return nullptr;
1230 OUString XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
1232 if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
1233 return pField->GetFieldName();
1234 return OUString();
1237 // records --------------------------------------------------------------------
1239 void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
1241 rStrm >> maPTInfo;
1243 GetAddressConverter().ConvertRange(
1244 maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
1246 mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
1247 mxCurrField.reset();
1250 void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
1252 sal_uInt16 nFieldCount = GetFieldCount();
1253 if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
1255 // cache index for the field is equal to the SXVD record index
1256 mxCurrField.reset( new XclImpPTField( *this, nFieldCount ) );
1257 maFields.push_back( mxCurrField );
1258 mxCurrField->ReadSxvd( rStrm );
1259 // add visible name of new field to list of visible names
1260 maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
1261 OSL_ENSURE( maFields.size() == maVisFieldNames.size(),
1262 "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
1264 else
1265 mxCurrField.reset();
1268 void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
1270 if( mxCurrField )
1271 mxCurrField->ReadSxvi( rStrm );
1274 void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
1276 if( mxCurrField )
1277 mxCurrField->ReadSxvdex( rStrm );
1280 void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
1282 mxCurrField.reset();
1284 // find the index vector to fill (row SXIVD doesn't exist without row fields)
1285 ScfUInt16Vec* pFieldVec = nullptr;
1286 if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
1287 pFieldVec = &maRowFields;
1288 else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
1289 pFieldVec = &maColFields;
1291 // fill the vector from record data
1292 if( pFieldVec )
1294 sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
1295 pFieldVec->reserve( nSize );
1296 for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
1298 sal_uInt16 nFieldIdx;
1299 nFieldIdx = rStrm.ReaduInt16();
1300 pFieldVec->push_back( nFieldIdx );
1302 // set orientation at special data orientation field
1303 if( nFieldIdx == EXC_SXIVD_DATA )
1305 sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
1306 maDataOrientField.SetAxes( nAxis );
1312 void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
1314 mxCurrField.reset();
1316 sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
1317 for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
1319 XclPTPageFieldInfo aPageInfo;
1320 rStrm >> aPageInfo;
1321 if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
1323 maPageFields.push_back( aPageInfo.mnField );
1324 pField->SetPageFieldInfo( aPageInfo );
1326 GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
1330 void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
1332 mxCurrField.reset();
1334 XclPTDataFieldInfo aDataInfo;
1335 rStrm >> aDataInfo;
1336 if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
1338 maOrigDataFields.push_back( aDataInfo.mnField );
1339 // DataPilot does not support double data fields -> add first appearance to index list only
1340 if( !pField->HasDataFieldInfo() )
1341 maFiltDataFields.push_back( aDataInfo.mnField );
1342 pField->AddDataFieldInfo( aDataInfo );
1346 void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
1348 rStrm >> maPTExtInfo;
1351 void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
1353 rStrm >> maPTViewEx9Info;
1356 void XclImpPivotTable::Convert()
1358 if( !mxPCache || !mxPCache->IsValid() )
1359 return;
1361 ScDPSaveData aSaveData;
1363 // *** global settings ***
1365 aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
1366 aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
1367 aSaveData.SetFilterButton( false );
1368 aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
1370 // *** fields ***
1372 ScfUInt16Vec::const_iterator aIt, aEnd;
1374 // row fields
1375 for( aIt = maRowFields.begin(), aEnd = maRowFields.end(); aIt != aEnd; ++aIt )
1376 if( const XclImpPTField* pField = GetField( *aIt ) )
1377 pField->ConvertRowColField( aSaveData );
1379 // column fields
1380 for( aIt = maColFields.begin(), aEnd = maColFields.end(); aIt != aEnd; ++aIt )
1381 if( const XclImpPTField* pField = GetField( *aIt ) )
1382 pField->ConvertRowColField( aSaveData );
1384 // page fields
1385 for( aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
1386 if( const XclImpPTField* pField = GetField( *aIt ) )
1387 pField->ConvertPageField( aSaveData );
1389 // We need to import hidden fields because hidden fields may contain
1390 // special settings for subtotals (aggregation function, filters, custom
1391 // name etc.) and members (hidden, custom name etc.).
1393 // hidden fields
1394 for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
1395 if( const XclImpPTField* pField = GetField( nField ) )
1396 if (!pField->GetAxes())
1397 pField->ConvertHiddenField( aSaveData );
1399 // data fields
1400 for( aIt = maFiltDataFields.begin(), aEnd = maFiltDataFields.end(); aIt != aEnd; ++aIt )
1401 if( const XclImpPTField* pField = GetField( *aIt ) )
1402 pField->ConvertDataField( aSaveData );
1404 // *** insert into Calc document ***
1406 // create source descriptor
1407 ScSheetSourceDesc aDesc(&GetDocRef());
1408 const OUString& rSrcName = mxPCache->GetSourceRangeName();
1409 if (!rSrcName.isEmpty())
1410 // Range name is the data source.
1411 aDesc.SetRangeName(rSrcName);
1412 else
1413 // Normal cell range.
1414 aDesc.SetSourceRange(mxPCache->GetSourceRange());
1416 // adjust output range to include the page fields
1417 ScRange aOutRange( maOutScRange );
1418 if( !maPageFields.empty() )
1420 SCsROW nDecRows = ::std::min< SCsROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
1421 aOutRange.aStart.IncRow( -nDecRows );
1424 // create the DataPilot
1425 ScDPObject* pDPObj = new ScDPObject( &GetDocRef() );
1426 pDPObj->SetName( maPTInfo.maTableName );
1427 if (!maPTInfo.maDataName.isEmpty())
1428 aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
1430 if (!maPTViewEx9Info.maGrandTotalName.isEmpty())
1431 aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
1433 pDPObj->SetSaveData( aSaveData );
1434 pDPObj->SetSheetDesc( aDesc );
1435 pDPObj->SetOutRange( aOutRange );
1436 pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
1438 GetDoc().GetDPCollection()->InsertNewTable(pDPObj);
1439 mpDPObj = pDPObj;
1441 ApplyMergeFlags(aOutRange, aSaveData);
1444 void XclImpPivotTable::MaybeRefresh()
1446 if (mpDPObj && mxPCache->IsRefreshOnLoad())
1448 // 'refresh table on load' flag is set. Refresh the table now. Some
1449 // Excel files contain partial table output when this flag is set.
1450 ScRange aOutRange = mpDPObj->GetOutRange();
1451 mpDPObj->Output(aOutRange.aStart);
1455 void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
1457 // Apply merge flags for various datapilot controls.
1459 ScDPOutputGeometry aGeometry(rOutRange, false);
1460 aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
1461 aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
1462 aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
1463 aGeometry.setRowFieldCount(maPTInfo.mnRowFields);
1465 ScDocument& rDoc = GetDoc();
1467 vector<const ScDPSaveDimension*> aFieldDims;
1468 vector<ScAddress> aFieldBtns;
1470 aGeometry.getPageFieldPositions(aFieldBtns);
1471 vector<ScAddress>::const_iterator itr = aFieldBtns.begin(), itrEnd = aFieldBtns.end();
1472 for (; itr != itrEnd; ++itr)
1474 rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), ScMF::Button);
1476 ScMF nMFlag = ScMF::ButtonPopup;
1477 OUString aName = rDoc.GetString(itr->Col(), itr->Row(), itr->Tab());
1478 if (rSaveData.HasInvisibleMember(aName))
1479 nMFlag |= ScMF::HiddenMember;
1481 rDoc.ApplyFlagsTab(itr->Col()+1, itr->Row(), itr->Col()+1, itr->Row(), itr->Tab(), nMFlag);
1484 aGeometry.getColumnFieldPositions(aFieldBtns);
1485 rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aFieldDims);
1486 if (aFieldBtns.size() == aFieldDims.size())
1488 itr = aFieldBtns.begin();
1489 itrEnd = aFieldBtns.end();
1490 vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
1491 for (; itr != itrEnd; ++itr, ++itDim)
1493 ScMF nMFlag = ScMF::Button;
1494 const ScDPSaveDimension* pDim = *itDim;
1495 if (pDim->HasInvisibleMember())
1496 nMFlag |= ScMF::HiddenMember;
1497 if (!pDim->IsDataLayout())
1498 nMFlag |= ScMF::ButtonPopup;
1499 rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag);
1503 aGeometry.getRowFieldPositions(aFieldBtns);
1504 rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aFieldDims);
1505 if (aFieldBtns.size() == aFieldDims.size())
1507 itr = aFieldBtns.begin();
1508 itrEnd = aFieldBtns.end();
1509 vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
1510 for (; itr != itrEnd; ++itr, ++itDim)
1512 ScMF nMFlag = ScMF::Button;
1513 const ScDPSaveDimension* pDim = *itDim;
1514 if (pDim->HasInvisibleMember())
1515 nMFlag |= ScMF::HiddenMember;
1516 if (!pDim->IsDataLayout())
1517 nMFlag |= ScMF::ButtonPopup;
1518 rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag);
1523 XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
1524 XclImpRoot( rRoot )
1528 XclImpPivotTableManager::~XclImpPivotTableManager()
1532 // pivot cache records --------------------------------------------------------
1534 XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
1536 XclImpPivotCacheRef xPCache;
1537 if( nCacheIdx < maPCaches.size() )
1538 xPCache = maPCaches[ nCacheIdx ];
1539 return xPCache;
1542 void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
1544 XclImpPivotCacheRef xPCache( new XclImpPivotCache( GetRoot() ) );
1545 maPCaches.push_back( xPCache );
1546 xPCache->ReadSxidstm( rStrm );
1549 void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
1551 if( !maPCaches.empty() )
1552 maPCaches.back()->ReadSxvs( rStrm );
1555 void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
1557 if( !maPCaches.empty() )
1558 maPCaches.back()->ReadDconref( rStrm );
1561 void XclImpPivotTableManager::ReadDConName( XclImpStream& rStrm )
1563 if( !maPCaches.empty() )
1564 maPCaches.back()->ReadDConName( rStrm );
1567 // pivot table records --------------------------------------------------------
1569 void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
1571 XclImpPivotTableRef xPTable( new XclImpPivotTable( GetRoot() ) );
1572 maPTables.push_back( xPTable );
1573 xPTable->ReadSxview( rStrm );
1576 void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
1578 if( !maPTables.empty() )
1579 maPTables.back()->ReadSxvd( rStrm );
1582 void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
1584 if( !maPTables.empty() )
1585 maPTables.back()->ReadSxvdex( rStrm );
1588 void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
1590 if( !maPTables.empty() )
1591 maPTables.back()->ReadSxivd( rStrm );
1594 void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
1596 if( !maPTables.empty() )
1597 maPTables.back()->ReadSxpi( rStrm );
1600 void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
1602 if( !maPTables.empty() )
1603 maPTables.back()->ReadSxdi( rStrm );
1606 void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
1608 if( !maPTables.empty() )
1609 maPTables.back()->ReadSxvi( rStrm );
1612 void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
1614 if( !maPTables.empty() )
1615 maPTables.back()->ReadSxex( rStrm );
1618 void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
1620 if( !maPTables.empty() )
1621 maPTables.back()->ReadSxViewEx9( rStrm );
1624 void XclImpPivotTableManager::ReadPivotCaches( XclImpStream& rStrm )
1626 for( XclImpPivotCacheVec::iterator aIt = maPCaches.begin(), aEnd = maPCaches.end(); aIt != aEnd; ++aIt )
1627 (*aIt)->ReadPivotCacheStream( rStrm );
1630 void XclImpPivotTableManager::ConvertPivotTables()
1632 for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
1633 (*aIt)->Convert();
1636 void XclImpPivotTableManager::MaybeRefreshPivotTables()
1638 for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
1639 (*aIt)->MaybeRefresh();
1642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */