update dev300-m58
[ooovba.git] / sc / source / filter / excel / xepivot.cxx
blobe475661652babf8710bc1ff9c31274b53906e6b2
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xepivot.cxx,v $
10 * $Revision: 1.22 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include "xepivot.hxx"
34 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
35 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
36 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
37 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
38 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
40 #include <algorithm>
41 #include <math.h>
43 #include <rtl/math.hxx>
44 #include <tools/date.hxx>
45 #include <svtools/zformat.hxx>
46 #include <sot/storage.hxx>
47 #include "document.hxx"
48 #include "dpobject.hxx"
49 #include "dpsave.hxx"
50 #include "dpdimsave.hxx"
51 #include "dpshttab.hxx"
52 #include "globstr.hrc"
53 #include "fapihelper.hxx"
54 #include "xestring.hxx"
55 #include "xelink.hxx"
57 #include <oox/core/tokens.hxx>
59 using ::com::sun::star::sheet::DataPilotFieldOrientation;
60 using ::com::sun::star::sheet::DataPilotFieldOrientation_HIDDEN;
61 using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
62 using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
63 using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
64 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
65 using ::com::sun::star::sheet::GeneralFunction;
66 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
67 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
68 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
69 using ::com::sun::star::sheet::DataPilotFieldReference;
70 using ::rtl::OUString;
72 using ::rtl::OString;
73 using ::rtl::OUString;
74 using ::rtl::OUStringBuffer;
76 // ============================================================================
77 // Pivot cache
78 // ============================================================================
80 namespace {
82 // constants to track occurence of specific data types
83 const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
84 const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
85 const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
86 const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
88 /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
89 static const sal_uInt16 spnPCItemFlags[] =
90 { // STR DBL INT DAT
91 EXC_SXFIELD_DATA_NONE, //
92 EXC_SXFIELD_DATA_STR, // x
93 EXC_SXFIELD_DATA_INT, // x
94 EXC_SXFIELD_DATA_STR_INT, // x x
95 EXC_SXFIELD_DATA_DBL, // x
96 EXC_SXFIELD_DATA_STR_DBL, // x x
97 EXC_SXFIELD_DATA_INT, // x x
98 EXC_SXFIELD_DATA_STR_INT, // x x x
99 EXC_SXFIELD_DATA_DATE, // x
100 EXC_SXFIELD_DATA_DATE_STR, // x x
101 EXC_SXFIELD_DATA_DATE_NUM, // x x
102 EXC_SXFIELD_DATA_DATE_STR, // x x x
103 EXC_SXFIELD_DATA_DATE_NUM, // x x
104 EXC_SXFIELD_DATA_DATE_STR, // x x x
105 EXC_SXFIELD_DATA_DATE_NUM, // x x x
106 EXC_SXFIELD_DATA_DATE_STR // x x x x
109 } // namespace
111 // ----------------------------------------------------------------------------
113 XclExpPCItem::XclExpPCItem( const String& rText ) :
114 XclExpRecord( (rText.Len() > 0) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
115 mnTypeFlag( EXC_PCITEM_DATA_STRING )
117 if( rText.Len() )
118 SetText( rText );
119 else
120 SetEmpty();
123 XclExpPCItem::XclExpPCItem( double fValue ) :
124 XclExpRecord( EXC_ID_SXDOUBLE, 8 )
126 SetDouble( fValue );
127 mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
128 EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
131 XclExpPCItem::XclExpPCItem( const DateTime& rDateTime ) :
132 XclExpRecord( EXC_ID_SXDATETIME, 8 )
134 SetDateTime( rDateTime );
135 mnTypeFlag = EXC_PCITEM_DATA_DATE;
138 XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
139 XclExpRecord( EXC_ID_SXINTEGER, 2 ),
140 mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
142 SetInteger( nValue );
145 XclExpPCItem::XclExpPCItem( bool bValue ) :
146 XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
147 mnTypeFlag( EXC_PCITEM_DATA_STRING )
149 SetBool( bValue );
152 // ----------------------------------------------------------------------------
154 bool XclExpPCItem::EqualsText( const String& rText ) const
156 return (rText.Len() == 0) ? IsEmpty() : (GetText() && (*GetText() == rText));
159 bool XclExpPCItem::EqualsDouble( double fValue ) const
161 return GetDouble() && (*GetDouble() == fValue);
164 bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
166 return GetDateTime() && (*GetDateTime() == rDateTime);
169 bool XclExpPCItem::EqualsBool( bool bValue ) const
171 return GetBool() && (*GetBool() == bValue);
174 // ----------------------------------------------------------------------------
176 void XclExpPCItem::WriteBody( XclExpStream& rStrm )
178 if( const String* pText = GetText() )
180 rStrm << XclExpString( *pText );
182 else if( const double* pfValue = GetDouble() )
184 rStrm << *pfValue;
186 else if( const sal_Int16* pnValue = GetInteger() )
188 rStrm << *pnValue;
190 else if( const DateTime* pDateTime = GetDateTime() )
192 sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
193 sal_uInt16 nMonth = static_cast< sal_uInt16 >( pDateTime->GetMonth() );
194 sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
195 sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
196 sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
197 sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
198 if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
199 rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
201 else if( const bool* pbValue = GetBool() )
203 rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
205 else
207 // nothing to do for SXEMPTY
208 DBG_ASSERT( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
212 // ============================================================================
214 XclExpPCField::XclExpPCField(
215 const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
216 const ScDPObject& rDPObj, const ScRange& rRange ) :
217 XclExpRecord( EXC_ID_SXFIELD ),
218 XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
219 XclExpRoot( rRoot ),
220 mrPCache( rPCache ),
221 mnTypeFlags( 0 )
223 // general settings for the standard field, insert all items from source range
224 InitStandardField( rRange );
226 // add special settings for inplace numeric grouping
227 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
229 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
231 if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
233 const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
234 const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
235 DBG_ASSERT( !rNumInfo.Enable || !rDateInfo.Enable,
236 "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
238 if( rNumInfo.Enable )
239 InitNumGroupField( rDPObj, rNumInfo );
240 else if( rDateInfo.Enable )
241 InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
246 // final settings (flags, item numbers)
247 Finalize();
250 XclExpPCField::XclExpPCField(
251 const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
252 const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
253 XclExpRecord( EXC_ID_SXFIELD ),
254 XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
255 XclExpRoot( rRoot ),
256 mrPCache( rPCache ),
257 mnTypeFlags( 0 )
259 // add base field info (always using first base field, not predecessor of this field) ***
260 DBG_ASSERT( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
261 "XclExpPCField::FillFromGroup - wrong base cache field" );
262 maFieldInfo.maName = rGroupDim.GetGroupDimName();
263 maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
265 // add standard group info or date group info
266 const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
267 if( rDateInfo.Enable && (rGroupDim.GetDatePart() != 0) )
268 InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
269 else
270 InitStdGroupField( rBaseField, rGroupDim );
272 // final settings (flags, item numbers)
273 Finalize();
276 XclExpPCField::~XclExpPCField()
280 void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
282 DBG_ASSERT( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
283 "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
284 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
285 maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
288 sal_uInt16 XclExpPCField::GetItemCount() const
290 return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
293 const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
295 return GetVisItemList().GetRecord( nItemIdx ).get();
298 sal_uInt16 XclExpPCField::GetItemIndex( const String& rItemName ) const
300 const XclExpPCItemList& rItemList = GetVisItemList();
301 for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
302 if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
303 return static_cast< sal_uInt16 >( nPos );
304 return EXC_PC_NOITEM;
307 sal_Size XclExpPCField::GetIndexSize() const
309 return Has16BitIndexes() ? 2 : 1;
312 void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
314 // only standard fields write item indexes
315 if( nSrcRow < maIndexVec.size() )
317 sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
318 if( Has16BitIndexes() )
319 rStrm << nIndex;
320 else
321 rStrm << static_cast< sal_uInt8 >( nIndex );
325 void XclExpPCField::Save( XclExpStream& rStrm )
327 DBG_ASSERT( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
328 // SXFIELD
329 XclExpRecord::Save( rStrm );
330 // SXFDBTYPE
331 XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
332 // list of grouping items
333 maGroupItemList.Save( rStrm );
334 // SXGROUPINFO
335 WriteSxgroupinfo( rStrm );
336 // SXNUMGROUP and additional grouping items (grouping limit settings)
337 WriteSxnumgroup( rStrm );
338 // list of original items
339 maOrigItemList.Save( rStrm );
342 // private --------------------------------------------------------------------
344 const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
346 DBG_ASSERT( IsStandardField() == maGroupItemList.IsEmpty(),
347 "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
348 return IsStandardField() ? maOrigItemList : maGroupItemList;
351 void XclExpPCField::InitStandardField( const ScRange& rRange )
353 DBG_ASSERT( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
354 DBG_ASSERT( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
356 ScDocument& rDoc = GetDoc();
357 SvNumberFormatter& rFormatter = GetFormatter();
359 // field name is in top cell of the range
360 ScAddress aPos( rRange.aStart );
361 rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), maFieldInfo.maName );
362 // #i76047# maximum field name length in pivot cache is 255
363 maFieldInfo.maName.Erase( ::std::min( maFieldInfo.maName.Len(), EXC_PC_MAXSTRLEN ) );
365 // loop over all cells, create pivot cache items
366 for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
368 if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
370 double fValue = rDoc.GetValue( aPos );
371 short nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( aPos ) );
372 if( nFmtType == NUMBERFORMAT_LOGICAL )
373 InsertOrigBoolItem( fValue != 0 );
374 else if( nFmtType & NUMBERFORMAT_DATETIME )
375 InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ) );
376 else
377 InsertOrigDoubleItem( fValue );
379 else
381 String aText;
382 rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), aText );
383 InsertOrigTextItem( aText );
388 void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
390 DBG_ASSERT( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
392 maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
393 maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
395 // loop over all groups of this field
396 for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
398 if( const ScDPSaveGroupItem* pGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx ) )
400 // the index of the new item containing the grouping name
401 sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
402 // loop over all elements of one group
403 for( size_t nElemIdx = 0, nElemCount = pGroupItem->GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
405 if( const String* pElemName = pGroupItem->GetElementByIndex( nElemIdx ) )
407 // try to find the item that is part of the group in the base field
408 sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
409 if( nBaseItemIdx < maFieldInfo.mnBaseItems )
411 // add group name item only if there are any valid base items
412 if( nGroupItemIdx == EXC_PC_NOITEM )
413 nGroupItemIdx = InsertGroupItem( new XclExpPCItem( pGroupItem->GetGroupName() ) );
414 maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
421 // add items and base item indexes of all ungrouped elements
422 for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
423 // items that are not part of a group still have the EXC_PC_NOITEM entry
424 if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
425 // try to find the base item
426 if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
427 // create a clone of the base item, insert its index into item order list
428 maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
431 void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
433 DBG_ASSERT( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
434 DBG_ASSERT( rNumInfo.Enable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
436 // new field type, date type, limit settings (min/max/step/auto)
437 if( rNumInfo.DateValues )
439 // special case: group by days with step count
440 meFieldType = EXC_PCFIELD_DATEGROUP;
441 maNumGroupInfo.SetScDateType( com::sun::star::sheet::DataPilotFieldGroupBy::DAYS );
442 SetDateGroupLimit( rNumInfo, true );
444 else
446 meFieldType = EXC_PCFIELD_NUMGROUP;
447 maNumGroupInfo.SetNumType();
448 SetNumGroupLimit( rNumInfo );
451 // generate visible items
452 InsertNumDateGroupItems( rDPObj, rNumInfo );
455 void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
457 DBG_ASSERT( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
458 DBG_ASSERT( rDateInfo.Enable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
460 // new field type
461 meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
463 // date type, limit settings (min/max/step/auto)
464 maNumGroupInfo.SetScDateType( nDatePart );
465 SetDateGroupLimit( rDateInfo, false );
467 // generate visible items
468 InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
471 void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
473 DBG_ASSERT( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
474 maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
477 void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
479 size_t nItemIdx = maOrigItemList.GetSize();
480 maOrigItemList.AppendNewRecord( pNewItem );
481 InsertItemArrayIndex( nItemIdx );
482 mnTypeFlags |= pNewItem->GetTypeFlag();
485 void XclExpPCField::InsertOrigTextItem( const String& rText )
487 size_t nPos = 0;
488 bool bFound = false;
489 // #i76047# maximum item text length in pivot cache is 255
490 String aShortText( rText, 0, ::std::min( rText.Len(), EXC_PC_MAXSTRLEN ) );
491 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
492 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) == true )
493 InsertItemArrayIndex( nPos );
494 if( !bFound )
495 InsertOrigItem( new XclExpPCItem( aShortText ) );
498 void XclExpPCField::InsertOrigDoubleItem( double fValue )
500 size_t nPos = 0;
501 bool bFound = false;
502 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
503 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) == true )
504 InsertItemArrayIndex( nPos );
505 if( !bFound )
506 InsertOrigItem( new XclExpPCItem( fValue ) );
509 void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime )
511 size_t nPos = 0;
512 bool bFound = false;
513 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
514 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) == true )
515 InsertItemArrayIndex( nPos );
516 if( !bFound )
517 InsertOrigItem( new XclExpPCItem( rDateTime ) );
520 void XclExpPCField::InsertOrigBoolItem( bool bValue )
522 size_t nPos = 0;
523 bool bFound = false;
524 for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
525 if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) == true )
526 InsertItemArrayIndex( nPos );
527 if( !bFound )
528 InsertOrigItem( new XclExpPCItem( bValue ) );
531 sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
533 maGroupItemList.AppendNewRecord( pNewItem );
534 return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
537 void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
539 DBG_ASSERT( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
540 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
542 // get the string collection with original source elements
543 ScSheetDPData aDPData( GetDocPtr(), *pSrcDesc );
544 const TypedScStrCollection& rOrigColl = aDPData.GetColumnEntries( static_cast< long >( GetBaseFieldIndex() ) );
546 // get the string collection with generated grouping elements
547 ScDPNumGroupDimension aTmpDim( rNumInfo );
548 if( nDatePart != 0 )
549 aTmpDim.MakeDateHelper( rNumInfo, nDatePart );
550 const TypedScStrCollection& rGroupColl = aTmpDim.GetNumEntries( rOrigColl, GetDocPtr() );
551 for( USHORT nIdx = 0, nCount = rGroupColl.GetCount(); nIdx < nCount; ++nIdx )
552 if( const TypedStrData* pStrData = rGroupColl[ nIdx ] )
553 InsertGroupItem( new XclExpPCItem( pStrData->GetString() ) );
557 void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
559 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.AutoStart );
560 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.AutoEnd );
561 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Start ) );
562 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.End ) );
563 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Step ) );
566 void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
568 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.AutoStart );
569 ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.AutoEnd );
570 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.Start ) ) );
571 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.End ) ) );
572 sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.Step, 1, SAL_MAX_INT16 ) : 1;
573 maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
576 void XclExpPCField::Finalize()
578 // flags
579 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
580 // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
581 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
582 ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
583 /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
584 for the current combination of item types is added to the flags. */
585 ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
587 // item count fields
588 maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
589 maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
590 // maFieldInfo.mnBaseItems set in InitStdGroupField()
591 maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
594 void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
596 if( IsNumGroupField() || IsDateGroupField() )
598 // SXNUMGROUP record
599 rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
600 rStrm << maNumGroupInfo;
601 rStrm.EndRecord();
603 // limits (min/max/step) for numeric grouping
604 DBG_ASSERT( maNumGroupLimits.GetSize() == 3,
605 "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
606 maNumGroupLimits.Save( rStrm );
610 void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
612 DBG_ASSERT( IsStdGroupField() != maGroupOrder.empty(),
613 "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
614 if( IsStdGroupField() && !maGroupOrder.empty() )
616 rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
617 for( ScfUInt16Vec::const_iterator aIt = maGroupOrder.begin(), aEnd = maGroupOrder.end(); aIt != aEnd; ++aIt )
618 rStrm << *aIt;
619 rStrm.EndRecord();
623 void XclExpPCField::WriteBody( XclExpStream& rStrm )
625 rStrm << maFieldInfo;
628 // ============================================================================
630 XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
631 XclExpRoot( rRoot ),
632 mnListIdx( nListIdx ),
633 mbValid( false )
635 // source from sheet only
636 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
638 /* maOrigSrcRange: Range received from the DataPilot object.
639 maExpSrcRange: Range written to the DCONREF record.
640 maDocSrcRange: Range used to get source data from Calc document.
641 This range may be shorter than maExpSrcRange to improve export
642 performance (#i22541#). */
643 maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->aSourceRange;
645 // internal sheet data only
646 SCTAB nScTab = maExpSrcRange.aStart.Tab();
647 if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) )
649 // ValidateRange() restricts source range to valid Excel limits
650 if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
652 // #i22541# skip empty cell areas (performance)
653 SCCOL nDocCol1, nDocCol2;
654 SCROW nDocRow1, nDocRow2;
655 GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
656 GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
657 SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
658 SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
659 SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
660 SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
662 // #i22541# do not store index list for too big ranges
663 if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
664 ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
666 // adjust row indexes, keep one row of empty area to surely have the empty cache item
667 if( nSrcRow1 < nDocRow1 )
668 nSrcRow1 = nDocRow1 - 1;
669 if( nSrcRow2 > nDocRow2 )
670 nSrcRow2 = nDocRow2 + 1;
672 maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
673 maDocSrcRange.aStart.SetRow( nSrcRow1 );
674 maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
675 maDocSrcRange.aEnd.SetRow( nSrcRow2 );
677 GetDoc().GetName( nScTab, maTabName );
678 maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
679 maPCInfo.mnStrmId = nListIdx + 1;
680 maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
682 AddFields( rDPObj );
684 mbValid = true;
690 bool XclExpPivotCache::HasItemIndexList() const
692 return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
695 sal_uInt16 XclExpPivotCache::GetFieldCount() const
697 return static_cast< sal_uInt16 >( maFieldList.GetSize() );
700 const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
702 return maFieldList.GetRecord( nFieldIdx ).get();
705 //UNUSED2009-05 const XclExpPCField* XclExpPivotCache::GetField( const String& rFieldName ) const
706 //UNUSED2009-05 {
707 //UNUSED2009-05 return const_cast< XclExpPivotCache* >( this )->GetFieldAcc( rFieldName );
708 //UNUSED2009-05 }
710 bool XclExpPivotCache::HasAddFields() const
712 // pivot cache can be shared, if there are no additional cache fields
713 return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
716 bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
718 /* For now, only sheet sources are supported, therefore it is enough to
719 compare the ScSheetSourceDesc. Later, there should be done more complicated
720 comparisons regarding the source type of rDPObj and this cache. */
721 if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
722 return pSrcDesc->aSourceRange == maOrigSrcRange;
723 return false;
726 void XclExpPivotCache::Save( XclExpStream& rStrm )
728 DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
729 // SXIDSTM
730 XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
731 // SXVS
732 XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
733 // DCONREF
734 WriteDconref( rStrm );
735 // create the pivot cache storage stream
736 WriteCacheStream();
739 void XclExpPivotCache::SaveXml( XclExpXmlStream& rStrm )
741 DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
742 sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
743 OUString sId = OUStringBuffer()
744 .appendAscii("rId")
745 .append( rStrm.GetUniqueIdOUString() )
746 .makeStringAndClear();
747 rWorkbook->startElement( XML_pivotCache,
748 XML_cacheId, OString::valueOf( (sal_Int32)maPCInfo.mnStrmId ).getStr(),
749 FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
750 FSEND );
751 // SXIDSTM
752 XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).SaveXml( rStrm );
753 // SXVS
754 XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).SaveXml( rStrm );
755 // DCONREF
756 // OOXTODO: WriteDconref( rStrm );
757 // create the pivot cache storage stream
758 // OOXTODO: WriteCacheStream();
759 rWorkbook->endElement( XML_pivotCache );
762 // private --------------------------------------------------------------------
764 XclExpPCField* XclExpPivotCache::GetFieldAcc( sal_uInt16 nFieldIdx )
766 return maFieldList.GetRecord( nFieldIdx ).get();
769 XclExpPCField* XclExpPivotCache::GetFieldAcc( const String& rFieldName )
771 XclExpPCField* pField = 0;
772 for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
773 if( maFieldList.GetRecord( nPos )->GetFieldName() == rFieldName )
774 pField = maFieldList.GetRecord( nPos ).get();
775 return pField;
778 void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
780 AddStdFields( rDPObj );
781 maPCInfo.mnStdFields = GetFieldCount();
782 AddGroupFields( rDPObj );
783 AddCalcFields( rDPObj );
784 maPCInfo.mnTotalFields = GetFieldCount();
787 void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
789 // if item index list is not written, used shortened source range (maDocSrcRange) for performance
790 const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
791 // create a standard pivot cache field for each source column
792 for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
794 ScRange aColRange( rRange );
795 aColRange.aStart.SetCol( nScCol );
796 aColRange.aEnd.SetCol( nScCol );
797 maFieldList.AppendNewRecord( new XclExpPCField(
798 GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) );
802 void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
804 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
806 if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
808 // loop over all existing standard fields to find their group fields
809 for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
811 if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) )
813 const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
814 XclExpPCField* pLastGroupField = pCurrStdField;
815 while( pGroupDim )
817 // insert the new grouping field
818 XclExpPCFieldRef xNewGroupField( new XclExpPCField(
819 GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
820 maFieldList.AppendRecord( xNewGroupField );
822 // register new grouping field at current grouping field, building a chain
823 pLastGroupField->SetGroupChildField( *xNewGroupField );
825 // next grouping dimension
826 pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
827 pLastGroupField = xNewGroupField.get();
835 void XclExpPivotCache::AddCalcFields( const ScDPObject& /*rDPObj*/ )
837 // not supported
840 void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
842 XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_STRING, &maTabName ) );
843 rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
844 rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
845 << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
846 << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
847 << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
848 << aRef
849 << sal_uInt8( 0 );
850 rStrm.EndRecord();
853 void XclExpPivotCache::WriteCacheStream()
855 SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
856 SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
857 if( xSvStrm.Is() )
859 XclExpStream aStrm( *xSvStrm, GetRoot() );
860 // SXDB
861 WriteSxdb( aStrm );
862 // SXDBEX
863 WriteSxdbex( aStrm );
864 // field list (SXFIELD and items)
865 maFieldList.Save( aStrm );
866 // index table (list of SXINDEXLIST)
867 WriteSxindexlistList( aStrm );
868 // EOF
869 XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
873 void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
875 rStrm.StartRecord( EXC_ID_SXDB, 21 );
876 rStrm << maPCInfo;
877 rStrm.EndRecord();
880 void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm ) const
882 rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
883 rStrm << EXC_SXDBEX_CREATION_DATE
884 << sal_uInt32( 0 ); // number of SXFORMULA records
885 rStrm.EndRecord();
888 void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
890 if( HasItemIndexList() )
892 sal_Size nRecSize = 0;
893 size_t nPos, nSize = maFieldList.GetSize();
894 for( nPos = 0; nPos < nSize; ++nPos )
895 nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
897 for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
899 rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
900 for( nPos = 0; nPos < nSize; ++nPos )
901 maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
902 rStrm.EndRecord();
907 // ============================================================================
908 // Pivot table
909 // ============================================================================
911 namespace {
913 // ----------------------------------------------------------------------------
915 /** Returns a display string for a data field containing the field name and aggregation function. */
916 String lclGetDataFieldCaption( const String& rFieldName, GeneralFunction eFunc )
918 String aCaption;
920 USHORT nResIdx = 0;
921 using namespace ::com::sun::star::sheet;
922 switch( eFunc )
924 case GeneralFunction_SUM: nResIdx = STR_FUN_TEXT_SUM; break;
925 case GeneralFunction_COUNT: nResIdx = STR_FUN_TEXT_COUNT; break;
926 case GeneralFunction_AVERAGE: nResIdx = STR_FUN_TEXT_AVG; break;
927 case GeneralFunction_MAX: nResIdx = STR_FUN_TEXT_MAX; break;
928 case GeneralFunction_MIN: nResIdx = STR_FUN_TEXT_MIN; break;
929 case GeneralFunction_PRODUCT: nResIdx = STR_FUN_TEXT_PRODUCT; break;
930 case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT; break;
931 case GeneralFunction_STDEV: nResIdx = STR_FUN_TEXT_STDDEV; break;
932 case GeneralFunction_STDEVP: nResIdx = STR_FUN_TEXT_STDDEV; break;
933 case GeneralFunction_VAR: nResIdx = STR_FUN_TEXT_VAR; break;
934 case GeneralFunction_VARP: nResIdx = STR_FUN_TEXT_VAR; break;
935 default:;
937 if( nResIdx )
938 aCaption.Assign( ScGlobal::GetRscString( nResIdx ) ).AppendAscii( RTL_CONSTASCII_STRINGPARAM( " - " ) );
939 aCaption.Append( rFieldName );
940 return aCaption;
943 // ----------------------------------------------------------------------------
945 } // namespace
947 // ============================================================================
949 XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
950 XclExpRecord( EXC_ID_SXVI, 8 ),
951 mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
953 maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
954 maItemInfo.mnCacheIdx = nCacheIdx;
955 maItemInfo.maVisName.mbUseCache = mpCacheItem != 0;
958 XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) :
959 XclExpRecord( EXC_ID_SXVI, 8 ),
960 mpCacheItem( 0 )
962 maItemInfo.mnType = nItemType;
963 maItemInfo.mnCacheIdx = nCacheIdx;
964 maItemInfo.maVisName.mbUseCache = bUseCache;
967 const String& XclExpPTItem::GetItemName() const
969 return mpCacheItem ? mpCacheItem->ConvertToText() : EMPTY_STRING;
972 void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
974 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, !rSaveMem.GetIsVisible() );
975 ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, !rSaveMem.GetShowDetails() );
977 // visible name
978 const OUString* pVisName = rSaveMem.GetLayoutName();
979 if (pVisName && !pVisName->equals(GetItemName()))
980 maItemInfo.SetVisName(*pVisName);
983 void XclExpPTItem::WriteBody( XclExpStream& rStrm )
985 rStrm << maItemInfo;
988 // ============================================================================
990 XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
991 mrPTable( rPTable ),
992 mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
994 maFieldInfo.mnCacheIdx = nCacheIdx;
996 // create field items
997 if( mpCacheField )
998 for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
999 maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
1000 maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
1003 // data access ----------------------------------------------------------------
1005 const String& XclExpPTField::GetFieldName() const
1007 return mpCacheField ? mpCacheField->GetFieldName() : EMPTY_STRING;
1010 sal_uInt16 XclExpPTField::GetFieldIndex() const
1012 // field index always equal to cache index
1013 return maFieldInfo.mnCacheIdx;
1016 sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
1018 DBG_ASSERT( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
1019 // will return 0xFFFF for empty vector -> ok
1020 return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
1023 //UNUSED2009-05 const XclExpPTItem* XclExpPTField::GetItem( const String& rName ) const
1024 //UNUSED2009-05 {
1025 //UNUSED2009-05 return const_cast< XclExpPTField* >( this )->GetItemAcc( rName );
1026 //UNUSED2009-05 }
1028 sal_uInt16 XclExpPTField::GetItemIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
1030 for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
1031 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1032 return static_cast< sal_uInt16 >( nPos );
1033 return nDefaultIdx;
1036 // fill data --------------------------------------------------------------
1038 /**
1039 * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
1040 * are not escaped at all.
1042 static OUString lcl_convertCalcSubtotalName(const OUString& rName)
1044 OUStringBuffer aBuf;
1045 const sal_Unicode* p = rName.getStr();
1046 sal_Int32 n = rName.getLength();
1047 bool bEscaped = false;
1048 for (sal_Int32 i = 0; i < n; ++i)
1050 const sal_Unicode c = p[i];
1051 if (!bEscaped && c == sal_Unicode('\\'))
1053 bEscaped = true;
1054 continue;
1057 aBuf.append(c);
1058 bEscaped = false;
1060 return aBuf.makeStringAndClear();
1063 void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1065 // orientation
1066 DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1067 DBG_ASSERT( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1068 maFieldInfo.AddApiOrient( eOrient );
1070 // show empty items
1071 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.GetShowEmpty() );
1073 // visible name
1074 const OUString* pLayoutName = rSaveDim.GetLayoutName();
1075 if (pLayoutName && !pLayoutName->equals(GetFieldName()))
1076 maFieldInfo.SetVisName(*pLayoutName);
1078 const rtl::OUString* pSubtotalName = rSaveDim.GetSubtotalName();
1079 if (pSubtotalName)
1081 OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1082 maFieldExtInfo.mpFieldTotalName.reset(new rtl::OUString(aSubName));
1085 // subtotals
1086 XclPTSubtotalVec aSubtotals;
1087 aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1088 for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1089 aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1090 maFieldInfo.SetSubtotals( aSubtotals );
1092 // sorting
1093 if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1095 maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1096 if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA )
1097 maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1098 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1101 // auto show
1102 if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1104 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1105 maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1106 maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1107 maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1110 // layout
1111 if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1113 maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1114 ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1117 // special page field properties
1118 if( eOrient == DataPilotFieldOrientation_PAGE )
1120 maPageInfo.mnField = GetFieldIndex();
1122 // selected item
1123 if( rSaveDim.HasCurrentPage() )
1124 maPageInfo.mnSelItem = GetItemIndex( rSaveDim.GetCurrentPage(), EXC_SXPI_ALLITEMS );
1125 else
1126 maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1129 // item properties
1130 const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1131 for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; i++)
1132 if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) )
1133 pItem->SetPropertiesFromMember( **i );
1136 void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1138 maDataInfoVec.push_back( XclPTDataFieldInfo() );
1139 XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1140 rDataInfo.mnField = GetFieldIndex();
1142 // orientation
1143 maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1145 // aggregation function
1146 GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() );
1147 rDataInfo.SetApiAggFunc( eFunc );
1149 // visible name
1150 const rtl::OUString* pVisName = rSaveDim.GetLayoutName();
1151 if (pVisName)
1152 rDataInfo.SetVisName(*pVisName);
1153 else
1154 rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1156 // result field reference
1157 if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1159 rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1160 rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1161 if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1163 rDataInfo.mnRefField = pRefField->GetFieldIndex();
1164 if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
1165 rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1170 void XclExpPTField::AppendSubtotalItems()
1172 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1173 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1174 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1175 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1176 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1177 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1178 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1179 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1180 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1181 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1182 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1183 if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1186 // records --------------------------------------------------------------------
1188 void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1190 rStrm << maPageInfo;
1193 void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1195 DBG_ASSERT( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1196 if( nDataInfoIdx < maDataInfoVec.size() )
1198 rStrm.StartRecord( EXC_ID_SXDI, 12 );
1199 rStrm << maDataInfoVec[ nDataInfoIdx ];
1200 rStrm.EndRecord();
1204 void XclExpPTField::Save( XclExpStream& rStrm )
1206 // SXVD
1207 WriteSxvd( rStrm );
1208 // list of SXVI records
1209 maItemList.Save( rStrm );
1210 // SXVDEX
1211 WriteSxvdex( rStrm );
1214 // private --------------------------------------------------------------------
1216 XclExpPTItem* XclExpPTField::GetItemAcc( const String& rName )
1218 XclExpPTItem* pItem = 0;
1219 for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1220 if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1221 pItem = maItemList.GetRecord( nPos ).get();
1222 return pItem;
1225 void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1227 maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) );
1228 ++maFieldInfo.mnItemCount;
1231 void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1233 rStrm.StartRecord( EXC_ID_SXVD, 10 );
1234 rStrm << maFieldInfo;
1235 rStrm.EndRecord();
1238 void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1240 rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1241 rStrm << maFieldExtInfo;
1242 rStrm.EndRecord();
1245 // ============================================================================
1247 XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
1248 XclExpRoot( rRoot ),
1249 mrPCache( rPCache ),
1250 maDataOrientField( *this, EXC_SXIVD_DATA ),
1251 mnOutScTab( 0 ),
1252 mbValid( false ),
1253 mbFilterBtn( false )
1255 const ScRange& rOutScRange = rDPObj.GetOutRange();
1256 if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1258 // DataPilot properties -----------------------------------------------
1260 // pivot table properties from DP object
1261 mnOutScTab = rOutScRange.aStart.Tab();
1262 maPTInfo.maTableName = rDPObj.GetName();
1263 maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1265 maPTViewEx9Info.Init( rDPObj );
1267 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1269 // additional properties from ScDPSaveData
1270 SetPropertiesFromDP( *pSaveData );
1272 // loop over all dimensions ---------------------------------------
1274 /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1275 for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1276 maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1278 const List& rDimList = pSaveData->GetDimensions();
1279 ULONG nDimIdx, nDimCount = rDimList.Count();
1281 /* 2) First process all data dimensions, they are needed for extended
1282 settings of row/column/page fields (sorting/auto show). */
1283 for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
1284 if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
1285 if( pSaveDim->GetOrientation() == DataPilotFieldOrientation_DATA )
1286 SetDataFieldPropertiesFromDim( *pSaveDim );
1288 /* 3) Row/column/page/hidden fields. */
1289 for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
1290 if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
1291 if( pSaveDim->GetOrientation() != DataPilotFieldOrientation_DATA )
1292 SetFieldPropertiesFromDim( *pSaveDim );
1294 // Finalize -------------------------------------------------------
1296 Finalize();
1297 mbValid = true;
1302 const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1304 return mrPCache.GetField( nCacheIdx );
1307 const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1309 return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
1312 const XclExpPTField* XclExpPivotTable::GetField( const String& rName ) const
1314 return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1317 sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
1319 for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1320 if( const XclExpPTField* pField = GetField( aIt->first ) )
1321 if( pField->GetFieldName() == rName )
1322 return static_cast< sal_uInt16 >( aIt - maDataFields.begin() );
1323 return nDefaultIdx;
1326 void XclExpPivotTable::Save( XclExpStream& rStrm )
1328 if( mbValid )
1330 // SXVIEW
1331 WriteSxview( rStrm );
1332 // pivot table fields (SXVD, SXVDEX, and item records)
1333 maFieldList.Save( rStrm );
1334 // SXIVD records for row and column fields
1335 WriteSxivd( rStrm, maRowFields );
1336 WriteSxivd( rStrm, maColFields );
1337 // SXPI
1338 WriteSxpi( rStrm );
1339 // list of SXDI records containing data field info
1340 WriteSxdiList( rStrm );
1341 // SXLI records
1342 WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1343 WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1344 // SXEX
1345 WriteSxex( rStrm );
1346 // QSISXTAG
1347 WriteQsiSxTag( rStrm );
1348 // SXVIEWEX9
1349 WriteSxViewEx9( rStrm );
1353 // private --------------------------------------------------------------------
1355 XclExpPTField* XclExpPivotTable::GetFieldAcc( const String& rName )
1357 XclExpPTField* pField = 0;
1358 for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1359 if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1360 pField = maFieldList.GetRecord( nPos ).get();
1361 return pField;
1364 XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1366 // data field orientation field?
1367 if( rSaveDim.IsDataLayout() )
1368 return &maDataOrientField;
1370 // a real dimension
1371 String aFieldName( rSaveDim.GetName() );
1372 return aFieldName.Len() ? GetFieldAcc( aFieldName ) : 0;
1375 // fill data --------------------------------------------------------------
1377 void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1379 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1380 ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1381 ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1382 mbFilterBtn = rSaveData.GetFilterButton();
1383 const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1384 if (!pDim)
1385 return;
1387 const rtl::OUString* pLayoutName = pDim->GetLayoutName();
1388 if (pLayoutName)
1389 maPTInfo.maDataName = *pLayoutName;
1390 else
1391 maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
1394 void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1396 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1398 // field properties
1399 pField->SetPropertiesFromDim( rSaveDim );
1401 // update the corresponding field position list
1402 DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1403 sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1404 bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1405 bool bMultiData = maDataFields.size() > 1;
1407 if( !bDataLayout || bMultiData ) switch( eOrient )
1409 case DataPilotFieldOrientation_ROW:
1410 maRowFields.push_back( nFieldIdx );
1411 if( bDataLayout )
1412 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1413 break;
1414 case DataPilotFieldOrientation_COLUMN:
1415 maColFields.push_back( nFieldIdx );
1416 if( bDataLayout )
1417 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1418 break;
1419 case DataPilotFieldOrientation_PAGE:
1420 maPageFields.push_back( nFieldIdx );
1421 DBG_ASSERT( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1422 break;
1423 case DataPilotFieldOrientation_DATA:
1424 DBG_ERRORFILE( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1425 break;
1426 default:;
1431 void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1433 if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1435 // field properties
1436 pField->SetDataPropertiesFromDim( rSaveDim );
1437 // update the data field position list
1438 maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) );
1442 void XclExpPivotTable::Finalize()
1444 // field numbers
1445 maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1446 maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1447 maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1448 maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1449 maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1451 maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1452 maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1454 // subtotal items
1455 for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1456 maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1458 // find data field orientation field
1459 maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1460 const ScfUInt16Vec* pFieldVec = 0;
1461 switch( maPTInfo.mnDataAxis )
1463 case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1464 case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1467 if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1469 ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1470 if( aIt != pFieldVec->end() )
1471 maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() );
1474 // single data field is always row oriented
1475 if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1476 maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1478 // update output range (initialized in ctor)
1479 sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1480 sal_uInt16& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1481 sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1482 sal_uInt16& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1483 // exclude page fields from output range
1484 rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1485 // exclude filter button from output range
1486 if( mbFilterBtn )
1487 ++rnXclRow1;
1488 // exclude empty row between (filter button and/or page fields) and table
1489 if( mbFilterBtn || maPTInfo.mnPageFields )
1490 ++rnXclRow1;
1492 // data area
1493 sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1494 sal_uInt16& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1495 rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1496 rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1497 if( maDataFields.empty() )
1498 ++rnDataXclRow;
1500 bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1501 if (bExtraHeaderRow)
1502 // Insert an extra row only when there is no column field.
1503 ++rnDataXclRow;
1505 rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1506 rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1507 maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1508 maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1510 // first heading
1511 maPTInfo.mnFirstHeadRow = rnXclRow1;
1512 if (bExtraHeaderRow)
1513 maPTInfo.mnFirstHeadRow += 2;
1516 // records ----------------------------------------------------------------
1518 void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1520 rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.Len() + maPTInfo.maDataName.Len() );
1521 rStrm << maPTInfo;
1522 rStrm.EndRecord();
1525 void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields ) const
1527 if( !rFields.empty() )
1529 rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1530 for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt )
1531 rStrm << *aIt;
1532 rStrm.EndRecord();
1536 void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1538 if( !maPageFields.empty() )
1540 rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1541 rStrm.SetSliceSize( 6 );
1542 for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
1544 XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt );
1545 if( xField.is() )
1546 xField->WriteSxpiEntry( rStrm );
1548 rStrm.EndRecord();
1552 void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1554 for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1556 XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first );
1557 if( xField.is() )
1558 xField->WriteSxdi( rStrm, aIt->second );
1562 void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const
1564 if( nLineCount > 0 )
1566 sal_uInt16 nLineSize = 8 + 2 * nIndexCount;
1567 rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1569 /* #158444# Excel expects the records to be filled completely, do not
1570 set a segment size... */
1571 // rStrm.SetSliceSize( nLineSize );
1573 for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1575 // #106598# Excel XP needs a partly initialized SXLI record
1576 rStrm << sal_uInt16( 0 ) // number of equal index entries
1577 << EXC_SXVI_TYPE_DATA
1578 << nIndexCount
1579 << EXC_SXLI_DEFAULTFLAGS;
1580 rStrm.WriteZeroBytes( 2 * nIndexCount );
1582 rStrm.EndRecord();
1586 void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1588 rStrm.StartRecord( EXC_ID_SXEX, 24 );
1589 rStrm << maPTExtInfo;
1590 rStrm.EndRecord();
1593 void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1595 rStrm.StartRecord( 0x0802, 32 );
1597 sal_uInt16 nRecordType = 0x0802;
1598 sal_uInt16 nDummyFlags = 0x0000;
1599 sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table
1601 rStrm << nRecordType << nDummyFlags << nTableType;
1603 // General flags
1604 bool bEnableRefresh = true;
1605 bool bPCacheInvalid = false;
1606 bool bOlapPTReport = false;
1608 sal_uInt16 nFlags = 0x0000;
1609 if (bEnableRefresh) nFlags |= 0x0001;
1610 if (bPCacheInvalid) nFlags |= 0x0002;
1611 if (bOlapPTReport) nFlags |= 0x0004;
1612 rStrm << nFlags;
1614 // Feature-specific options. The value differs depending on the table
1615 // type, but we assume the table type is always pivot table.
1616 sal_uInt32 nOptions = 0x00000000;
1617 bool bNoStencil = false;
1618 bool bHideTotal = false;
1619 bool bEmptyRows = false;
1620 bool bEmptyCols = false;
1621 if (bNoStencil) nOptions |= 0x00000001;
1622 if (bHideTotal) nOptions |= 0x00000002;
1623 if (bEmptyRows) nOptions |= 0x00000008;
1624 if (bEmptyCols) nOptions |= 0x00000010;
1625 rStrm << nOptions;
1627 enum ExcelVersion
1629 Excel2000 = 0,
1630 ExcelXP = 1,
1631 Excel2003 = 2,
1632 Excel2007 = 3
1634 ExcelVersion eXclVer = Excel2000;
1635 sal_uInt8 nOffsetBytes = 16;
1636 rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed
1637 << static_cast<sal_uInt8>(eXclVer) // minimum version to refresh
1638 << nOffsetBytes
1639 << static_cast<sal_uInt8>(eXclVer); // first version created
1641 rStrm << XclExpString(maPTInfo.maTableName);
1642 rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1644 rStrm.EndRecord();
1647 void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1649 // Until we sync the autoformat ids only export if using grid header layout
1650 // That could only have been set via xls import so far.
1651 if ( 0 == maPTViewEx9Info.mnGridLayout )
1653 rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1654 rStrm << maPTViewEx9Info;
1655 rStrm.EndRecord();
1659 // ============================================================================
1661 namespace {
1663 const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1665 /** Record wrapper class to write the pivot caches or pivot tables. */
1666 class XclExpPivotRecWrapper : public XclExpRecordBase
1668 public:
1669 explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1670 virtual void Save( XclExpStream& rStrm );
1671 virtual void SaveXml( XclExpXmlStream& rStrm );
1672 private:
1673 XclExpPivotTableManager& mrPTMgr;
1674 SCTAB mnScTab;
1677 XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1678 mrPTMgr( rPTMgr ),
1679 mnScTab( nScTab )
1683 void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1685 if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1686 mrPTMgr.WritePivotCaches( rStrm );
1687 else
1688 mrPTMgr.WritePivotTables( rStrm, mnScTab );
1691 void XclExpPivotRecWrapper::SaveXml( XclExpXmlStream& rStrm )
1693 if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1694 mrPTMgr.WritePivotCachesXml( rStrm );
1695 else
1696 mrPTMgr.WritePivotTablesXml( rStrm, mnScTab );
1699 } // namespace
1701 // ----------------------------------------------------------------------------
1703 XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1704 XclExpRoot( rRoot ),
1705 mbShareCaches( true )
1709 void XclExpPivotTableManager::CreatePivotTables()
1711 if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1712 for( USHORT nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1713 if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] )
1714 if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) )
1715 maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache ) );
1718 XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1720 return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
1723 XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1725 return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
1728 void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1730 maPCacheList.Save( rStrm );
1733 void XclExpPivotTableManager::WritePivotCachesXml( XclExpXmlStream& rStrm )
1735 if( maPCacheList.IsEmpty() )
1736 return;
1737 sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
1738 rWorkbook->startElement( XML_pivotCaches, FSEND );
1739 maPCacheList.SaveXml( rStrm );
1740 rWorkbook->endElement( XML_pivotCaches );
1743 void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1745 for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1747 XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1748 if( xPTable->GetScTab() == nScTab )
1749 xPTable->Save( rStrm );
1753 void XclExpPivotTableManager::WritePivotTablesXml( XclExpXmlStream& rStrm, SCTAB nScTab )
1755 for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1757 XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1758 if( xPTable->GetScTab() == nScTab )
1759 xPTable->SaveXml( rStrm );
1763 // private --------------------------------------------------------------------
1765 const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1767 // try to find a pivot cache with the same data source
1768 /* #i25110# In Excel, the pivot cache contains additional fields
1769 (i.e. grouping info, calculated fields). If the passed DataPilot object
1770 or the found cache contains this data, do not share the cache with
1771 multiple pivot tables. */
1772 if( mbShareCaches )
1774 if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1776 const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1777 // no dimension save data at all or save data does not contain grouping info
1778 if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1780 // check all existing pivot caches
1781 for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1783 XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
1784 // pivot cache does not have grouping info and source data is equal
1785 if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
1786 return xPCache.get();
1792 // create a new pivot cache
1793 sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1794 XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
1795 if( xNewPCache->IsValid() )
1797 maPCacheList.AppendRecord( xNewPCache );
1798 return xNewPCache.get();
1801 return 0;
1804 // ============================================================================