xref: /trunk/main/sc/source/filter/excel/xepivot.cxx (revision b77af630)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_scfilt.hxx"
26 #include "xepivot.hxx"
27 #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
28 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
29 #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
30 #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
31 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
32 
33 #include <algorithm>
34 #include <math.h>
35 
36 #include <rtl/math.hxx>
37 #include <tools/date.hxx>
38 #include <svl/zformat.hxx>
39 #include <sot/storage.hxx>
40 #include "document.hxx"
41 #include "dpobject.hxx"
42 #include "dpsave.hxx"
43 #include "dpdimsave.hxx"
44 #include "dpshttab.hxx"
45 #include "globstr.hrc"
46 #include "fapihelper.hxx"
47 #include "xestring.hxx"
48 #include "xelink.hxx"
49 
50 using namespace ::oox;
51 
52 using ::com::sun::star::sheet::DataPilotFieldOrientation;
53 using ::com::sun::star::sheet::DataPilotFieldOrientation_HIDDEN;
54 using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
55 using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
56 using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
57 using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
58 using ::com::sun::star::sheet::GeneralFunction;
59 using ::com::sun::star::sheet::DataPilotFieldSortInfo;
60 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
61 using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
62 using ::com::sun::star::sheet::DataPilotFieldReference;
63 using ::rtl::OUString;
64 
65 using ::rtl::OString;
66 using ::rtl::OUString;
67 using ::rtl::OUStringBuffer;
68 
69 // ============================================================================
70 // Pivot cache
71 // ============================================================================
72 
73 namespace {
74 
75 // constants to track occurrence of specific data types
76 const sal_uInt16 EXC_PCITEM_DATA_STRING     = 0x0001;   /// String, empty, boolean, error.
77 const sal_uInt16 EXC_PCITEM_DATA_DOUBLE     = 0x0002;   /// Double with fraction.
78 const sal_uInt16 EXC_PCITEM_DATA_INTEGER    = 0x0004;   /// Integer, double without fraction.
79 const sal_uInt16 EXC_PCITEM_DATA_DATE       = 0x0008;   /// Date, time, date/time.
80 
81 /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
82 static const sal_uInt16 spnPCItemFlags[] =
83 {                               // STR DBL INT DAT
84     EXC_SXFIELD_DATA_NONE,      //
85     EXC_SXFIELD_DATA_STR,       //  x
86     EXC_SXFIELD_DATA_INT,       //      x
87     EXC_SXFIELD_DATA_STR_INT,   //  x   x
88     EXC_SXFIELD_DATA_DBL,       //          x
89     EXC_SXFIELD_DATA_STR_DBL,   //  x       x
90     EXC_SXFIELD_DATA_INT,       //      x   x
91     EXC_SXFIELD_DATA_STR_INT,   //  x   x   x
92     EXC_SXFIELD_DATA_DATE,      //              x
93     EXC_SXFIELD_DATA_DATE_STR,  //  x           x
94     EXC_SXFIELD_DATA_DATE_NUM,  //      x       x
95     EXC_SXFIELD_DATA_DATE_STR,  //  x   x       x
96     EXC_SXFIELD_DATA_DATE_NUM,  //          x   x
97     EXC_SXFIELD_DATA_DATE_STR,  //  x       x   x
98     EXC_SXFIELD_DATA_DATE_NUM,  //      x   x   x
99     EXC_SXFIELD_DATA_DATE_STR   //  x   x   x   x
100 };
101 
102 } // namespace
103 
104 // ----------------------------------------------------------------------------
105 
XclExpPCItem(const String & rText)106 XclExpPCItem::XclExpPCItem( const String& rText ) :
107     XclExpRecord( (rText.Len() > 0) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
108     mnTypeFlag( EXC_PCITEM_DATA_STRING )
109 {
110     if( rText.Len() )
111         SetText( rText );
112     else
113         SetEmpty();
114 }
115 
XclExpPCItem(double fValue)116 XclExpPCItem::XclExpPCItem( double fValue ) :
117     XclExpRecord( EXC_ID_SXDOUBLE, 8 )
118 {
119     SetDouble( fValue );
120     mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
121         EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
122 }
123 
XclExpPCItem(const DateTime & rDateTime)124 XclExpPCItem::XclExpPCItem( const DateTime& rDateTime ) :
125     XclExpRecord( EXC_ID_SXDATETIME, 8 )
126 {
127     SetDateTime( rDateTime );
128     mnTypeFlag = EXC_PCITEM_DATA_DATE;
129 }
130 
XclExpPCItem(sal_Int16 nValue)131 XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
132     XclExpRecord( EXC_ID_SXINTEGER, 2 ),
133     mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
134 {
135     SetInteger( nValue );
136 }
137 
XclExpPCItem(bool bValue)138 XclExpPCItem::XclExpPCItem( bool bValue ) :
139     XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
140     mnTypeFlag( EXC_PCITEM_DATA_STRING )
141 {
142     SetBool( bValue );
143 }
144 
145 // ----------------------------------------------------------------------------
146 
EqualsText(const String & rText) const147 bool XclExpPCItem::EqualsText( const String& rText ) const
148 {
149     return (rText.Len() == 0) ? IsEmpty() : (GetText() && (*GetText() == rText));
150 }
151 
EqualsDouble(double fValue) const152 bool XclExpPCItem::EqualsDouble( double fValue ) const
153 {
154     return GetDouble() && (*GetDouble() == fValue);
155 }
156 
EqualsDateTime(const DateTime & rDateTime) const157 bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
158 {
159     return GetDateTime() && (*GetDateTime() == rDateTime);
160 }
161 
EqualsBool(bool bValue) const162 bool XclExpPCItem::EqualsBool( bool bValue ) const
163 {
164     return GetBool() && (*GetBool() == bValue);
165 }
166 
167 // ----------------------------------------------------------------------------
168 
WriteBody(XclExpStream & rStrm)169 void XclExpPCItem::WriteBody( XclExpStream& rStrm )
170 {
171     if( const String* pText = GetText() )
172     {
173         rStrm << XclExpString( *pText );
174     }
175     else if( const double* pfValue = GetDouble() )
176     {
177         rStrm << *pfValue;
178     }
179     else if( const sal_Int16* pnValue = GetInteger() )
180     {
181         rStrm << *pnValue;
182     }
183     else if( const DateTime* pDateTime = GetDateTime() )
184     {
185         sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
186         sal_uInt16 nMonth = static_cast< sal_uInt16 >( pDateTime->GetMonth() );
187         sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
188         sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
189         sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
190         sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
191         if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
192         rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
193     }
194     else if( const bool* pbValue = GetBool() )
195     {
196         rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
197     }
198     else
199     {
200         // nothing to do for SXEMPTY
201         DBG_ASSERT( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
202     }
203 }
204 
205 // ============================================================================
206 
XclExpPCField(const XclExpRoot & rRoot,const XclExpPivotCache & rPCache,sal_uInt16 nFieldIdx,const ScDPObject & rDPObj,const ScRange & rRange)207 XclExpPCField::XclExpPCField(
208         const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
209         const ScDPObject& rDPObj, const ScRange& rRange ) :
210     XclExpRecord( EXC_ID_SXFIELD ),
211     XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
212     XclExpRoot( rRoot ),
213     mrPCache( rPCache ),
214     mnTypeFlags( 0 )
215 {
216     // general settings for the standard field, insert all items from source range
217     InitStandardField( rRange );
218 
219     // add special settings for inplace numeric grouping
220     if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
221     {
222         if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
223         {
224             if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
225             {
226                 const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
227                 const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
228                 DBG_ASSERT( !rNumInfo.Enable || !rDateInfo.Enable,
229                     "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
230 
231                 if( rNumInfo.Enable )
232                     InitNumGroupField( rDPObj, rNumInfo );
233                 else if( rDateInfo.Enable )
234                     InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
235             }
236         }
237     }
238 
239     // final settings (flags, item numbers)
240     Finalize();
241 }
242 
XclExpPCField(const XclExpRoot & rRoot,const XclExpPivotCache & rPCache,sal_uInt16 nFieldIdx,const ScDPObject & rDPObj,const ScDPSaveGroupDimension & rGroupDim,const XclExpPCField & rBaseField)243 XclExpPCField::XclExpPCField(
244         const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
245         const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
246     XclExpRecord( EXC_ID_SXFIELD ),
247     XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
248     XclExpRoot( rRoot ),
249     mrPCache( rPCache ),
250     mnTypeFlags( 0 )
251 {
252     // add base field info (always using first base field, not predecessor of this field) ***
253     DBG_ASSERT( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
254         "XclExpPCField::FillFromGroup - wrong base cache field" );
255     maFieldInfo.maName = rGroupDim.GetGroupDimName();
256     maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
257 
258     // add standard group info or date group info
259     const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
260     if( rDateInfo.Enable && (rGroupDim.GetDatePart() != 0) )
261         InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
262     else
263         InitStdGroupField( rBaseField, rGroupDim );
264 
265     // final settings (flags, item numbers)
266     Finalize();
267 }
268 
~XclExpPCField()269 XclExpPCField::~XclExpPCField()
270 {
271 }
272 
SetGroupChildField(const XclExpPCField & rChildField)273 void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
274 {
275     DBG_ASSERT( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
276         "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
277     ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
278     maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
279 }
280 
GetItemCount() const281 sal_uInt16 XclExpPCField::GetItemCount() const
282 {
283     return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
284 }
285 
GetItem(sal_uInt16 nItemIdx) const286 const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
287 {
288     return GetVisItemList().GetRecord( nItemIdx ).get();
289 }
290 
GetItemIndex(const String & rItemName) const291 sal_uInt16 XclExpPCField::GetItemIndex( const String& rItemName ) const
292 {
293     const XclExpPCItemList& rItemList = GetVisItemList();
294     for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
295         if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
296             return static_cast< sal_uInt16 >( nPos );
297     return EXC_PC_NOITEM;
298 }
299 
GetIndexSize() const300 sal_Size XclExpPCField::GetIndexSize() const
301 {
302     return Has16BitIndexes() ? 2 : 1;
303 }
304 
WriteIndex(XclExpStream & rStrm,sal_uInt32 nSrcRow) const305 void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
306 {
307     // only standard fields write item indexes
308     if( nSrcRow < maIndexVec.size() )
309     {
310         sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
311         if( Has16BitIndexes() )
312             rStrm << nIndex;
313         else
314             rStrm << static_cast< sal_uInt8 >( nIndex );
315     }
316 }
317 
Save(XclExpStream & rStrm)318 void XclExpPCField::Save( XclExpStream& rStrm )
319 {
320     DBG_ASSERT( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
321     // SXFIELD
322     XclExpRecord::Save( rStrm );
323     // SXFDBTYPE
324     XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
325     // list of grouping items
326     maGroupItemList.Save( rStrm );
327     // SXGROUPINFO
328     WriteSxgroupinfo( rStrm );
329     // SXNUMGROUP and additional grouping items (grouping limit settings)
330     WriteSxnumgroup( rStrm );
331     // list of original items
332     maOrigItemList.Save( rStrm );
333 }
334 
335 // private --------------------------------------------------------------------
336 
GetVisItemList() const337 const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
338 {
339     DBG_ASSERT( IsStandardField() == maGroupItemList.IsEmpty(),
340         "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
341     return IsStandardField() ? maOrigItemList : maGroupItemList;
342 }
343 
InitStandardField(const ScRange & rRange)344 void XclExpPCField::InitStandardField( const ScRange& rRange )
345 {
346     DBG_ASSERT( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
347     DBG_ASSERT( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
348 
349     ScDocument& rDoc = GetDoc();
350     SvNumberFormatter& rFormatter = GetFormatter();
351 
352     // field name is in top cell of the range
353     ScAddress aPos( rRange.aStart );
354     rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), maFieldInfo.maName );
355     // #i76047# maximum field name length in pivot cache is 255
356     maFieldInfo.maName.Erase( ::std::min( maFieldInfo.maName.Len(), EXC_PC_MAXSTRLEN ) );
357 
358     // loop over all cells, create pivot cache items
359     for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
360     {
361         if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
362         {
363             double fValue = rDoc.GetValue( aPos );
364             short nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( aPos ) );
365             if( nFmtType == NUMBERFORMAT_LOGICAL )
366                 InsertOrigBoolItem( fValue != 0 );
367             else if( nFmtType & NUMBERFORMAT_DATETIME )
368                 InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ) );
369             else
370                 InsertOrigDoubleItem( fValue );
371         }
372         else
373         {
374             String aText;
375             rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), aText );
376             InsertOrigTextItem( aText );
377         }
378     }
379 }
380 
InitStdGroupField(const XclExpPCField & rBaseField,const ScDPSaveGroupDimension & rGroupDim)381 void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
382 {
383     DBG_ASSERT( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
384 
385     maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
386     maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
387 
388     // loop over all groups of this field
389     for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
390     {
391         if( const ScDPSaveGroupItem* pGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx ) )
392         {
393             // the index of the new item containing the grouping name
394             sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
395             // loop over all elements of one group
396             for( size_t nElemIdx = 0, nElemCount = pGroupItem->GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
397             {
398                 if( const String* pElemName = pGroupItem->GetElementByIndex( nElemIdx ) )
399                 {
400                     // try to find the item that is part of the group in the base field
401                     sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
402                     if( nBaseItemIdx < maFieldInfo.mnBaseItems )
403                     {
404                         // add group name item only if there are any valid base items
405                         if( nGroupItemIdx == EXC_PC_NOITEM )
406                             nGroupItemIdx = InsertGroupItem( new XclExpPCItem( pGroupItem->GetGroupName() ) );
407                         maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
408                     }
409                 }
410             }
411         }
412     }
413 
414     // add items and base item indexes of all ungrouped elements
415     for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
416         // items that are not part of a group still have the EXC_PC_NOITEM entry
417         if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
418             // try to find the base item
419             if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
420                 // create a clone of the base item, insert its index into item order list
421                 maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
422 }
423 
InitNumGroupField(const ScDPObject & rDPObj,const ScDPNumGroupInfo & rNumInfo)424 void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
425 {
426     DBG_ASSERT( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
427     DBG_ASSERT( rNumInfo.Enable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
428 
429     // new field type, date type, limit settings (min/max/step/auto)
430     if( rNumInfo.DateValues )
431     {
432         // special case: group by days with step count
433         meFieldType = EXC_PCFIELD_DATEGROUP;
434         maNumGroupInfo.SetScDateType( com::sun::star::sheet::DataPilotFieldGroupBy::DAYS );
435         SetDateGroupLimit( rNumInfo, true );
436     }
437     else
438     {
439         meFieldType = EXC_PCFIELD_NUMGROUP;
440         maNumGroupInfo.SetNumType();
441         SetNumGroupLimit( rNumInfo );
442     }
443 
444     // generate visible items
445     InsertNumDateGroupItems( rDPObj, rNumInfo );
446 }
447 
InitDateGroupField(const ScDPObject & rDPObj,const ScDPNumGroupInfo & rDateInfo,sal_Int32 nDatePart)448 void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
449 {
450     DBG_ASSERT( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
451     DBG_ASSERT( rDateInfo.Enable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
452 
453     // new field type
454     meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
455 
456     // date type, limit settings (min/max/step/auto)
457     maNumGroupInfo.SetScDateType( nDatePart );
458     SetDateGroupLimit( rDateInfo, false );
459 
460     // generate visible items
461     InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
462 }
463 
InsertItemArrayIndex(size_t nListPos)464 void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
465 {
466     DBG_ASSERT( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
467     maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
468 }
469 
InsertOrigItem(XclExpPCItem * pNewItem)470 void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
471 {
472     size_t nItemIdx = maOrigItemList.GetSize();
473     maOrigItemList.AppendNewRecord( pNewItem );
474     InsertItemArrayIndex( nItemIdx );
475     mnTypeFlags |= pNewItem->GetTypeFlag();
476 }
477 
InsertOrigTextItem(const String & rText)478 void XclExpPCField::InsertOrigTextItem( const String& rText )
479 {
480     size_t nPos = 0;
481     bool bFound = false;
482     // #i76047# maximum item text length in pivot cache is 255
483     String aShortText( rText, 0, ::std::min( rText.Len(), EXC_PC_MAXSTRLEN ) );
484     for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
485         if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) == true )
486             InsertItemArrayIndex( nPos );
487     if( !bFound )
488         InsertOrigItem( new XclExpPCItem( aShortText ) );
489 }
490 
InsertOrigDoubleItem(double fValue)491 void XclExpPCField::InsertOrigDoubleItem( double fValue )
492 {
493     size_t nPos = 0;
494     bool bFound = false;
495     for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
496         if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) == true )
497             InsertItemArrayIndex( nPos );
498     if( !bFound )
499         InsertOrigItem( new XclExpPCItem( fValue ) );
500 }
501 
InsertOrigDateTimeItem(const DateTime & rDateTime)502 void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime )
503 {
504     size_t nPos = 0;
505     bool bFound = false;
506     for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
507         if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) == true )
508             InsertItemArrayIndex( nPos );
509     if( !bFound )
510         InsertOrigItem( new XclExpPCItem( rDateTime ) );
511 }
512 
InsertOrigBoolItem(bool bValue)513 void XclExpPCField::InsertOrigBoolItem( bool bValue )
514 {
515     size_t nPos = 0;
516     bool bFound = false;
517     for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
518         if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) == true )
519             InsertItemArrayIndex( nPos );
520     if( !bFound )
521         InsertOrigItem( new XclExpPCItem( bValue ) );
522 }
523 
InsertGroupItem(XclExpPCItem * pNewItem)524 sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
525 {
526     maGroupItemList.AppendNewRecord( pNewItem );
527     return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
528 }
529 
InsertNumDateGroupItems(const ScDPObject & rDPObj,const ScDPNumGroupInfo & rNumInfo,sal_Int32 nDatePart)530 void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
531 {
532     DBG_ASSERT( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
533     if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
534     {
535         // get the string collection with original source elements
536         ScSheetDPData aDPData( GetDocPtr(), *pSrcDesc );
537         // Wang Xu Ming - DataPilot migration
538         // 2009-05-08
539         const std::vector< SCROW > aOrignial = aDPData.GetColumnEntries( static_cast< long >( GetBaseFieldIndex() ) );
540         // get the string collection with generated grouping elements
541         ScDPNumGroupDimension aTmpDim( rNumInfo );
542         if( nDatePart != 0 )
543             aTmpDim.MakeDateHelper( rNumInfo, nDatePart );
544         const std::vector< SCROW > aMemberIds = aTmpDim.GetNumEntries(  static_cast< SCCOL >( GetBaseFieldIndex() ), aDPData.GetCacheTable().GetCache(), aOrignial );
545         for ( size_t  nIdx = 0 ; nIdx < aMemberIds.size(); nIdx++ )
546         {
547             const ScDPItemData* pData = aDPData.GetMemberById(  static_cast< long >( GetBaseFieldIndex() ) , aMemberIds[ nIdx] );
548             if ( pData )
549                 InsertGroupItem( new XclExpPCItem( pData->GetString() ) );
550         }
551 // End Comments
552 	}
553 }
554 
SetNumGroupLimit(const ScDPNumGroupInfo & rNumInfo)555 void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
556 {
557     ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.AutoStart );
558     ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.AutoEnd );
559     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Start ) );
560     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.End ) );
561     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Step ) );
562 }
563 
SetDateGroupLimit(const ScDPNumGroupInfo & rDateInfo,bool bUseStep)564 void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
565 {
566     ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.AutoStart );
567     ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.AutoEnd );
568     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.Start ) ) );
569     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.End ) ) );
570     sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.Step, 1, SAL_MAX_INT16 ) : 1;
571     maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
572 }
573 
Finalize()574 void XclExpPCField::Finalize()
575 {
576     // flags
577     ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
578     // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
579     ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
580     ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
581     /*  mnTypeFlags is updated in all Insert***Item() functions. Now the flags
582         for the current combination of item types is added to the flags. */
583     ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
584 
585     // item count fields
586     maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
587     maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
588     // maFieldInfo.mnBaseItems set in InitStdGroupField()
589     maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
590 }
591 
WriteSxnumgroup(XclExpStream & rStrm)592 void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
593 {
594     if( IsNumGroupField() || IsDateGroupField() )
595     {
596         // SXNUMGROUP record
597         rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
598         rStrm << maNumGroupInfo;
599         rStrm.EndRecord();
600 
601         // limits (min/max/step) for numeric grouping
602         DBG_ASSERT( maNumGroupLimits.GetSize() == 3,
603             "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
604         maNumGroupLimits.Save( rStrm );
605     }
606 }
607 
WriteSxgroupinfo(XclExpStream & rStrm)608 void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
609 {
610     DBG_ASSERT( IsStdGroupField() != maGroupOrder.empty(),
611         "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
612     if( IsStdGroupField() && !maGroupOrder.empty() )
613     {
614         rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
615         for( ScfUInt16Vec::const_iterator aIt = maGroupOrder.begin(), aEnd = maGroupOrder.end(); aIt != aEnd; ++aIt )
616             rStrm << *aIt;
617         rStrm.EndRecord();
618     }
619 }
620 
WriteBody(XclExpStream & rStrm)621 void XclExpPCField::WriteBody( XclExpStream& rStrm )
622 {
623     rStrm << maFieldInfo;
624 }
625 
626 // ============================================================================
627 
XclExpPivotCache(const XclExpRoot & rRoot,const ScDPObject & rDPObj,sal_uInt16 nListIdx)628 XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
629     XclExpRoot( rRoot ),
630     mnListIdx( nListIdx ),
631     mbValid( false )
632 {
633     // source from sheet only
634     if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
635     {
636         /*  maOrigSrcRange: Range received from the DataPilot object.
637             maExpSrcRange: Range written to the DCONREF record.
638             maDocSrcRange: Range used to get source data from Calc document.
639                 This range may be shorter than maExpSrcRange to improve export
640                 performance (#i22541#). */
641         maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->aSourceRange;
642 
643         // internal sheet data only
644         SCTAB nScTab = maExpSrcRange.aStart.Tab();
645         if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) )
646         {
647             // ValidateRange() restricts source range to valid Excel limits
648             if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
649             {
650                 // #i22541# skip empty cell areas (performance)
651                 SCCOL nDocCol1, nDocCol2;
652                 SCROW nDocRow1, nDocRow2;
653                 GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
654                 GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
655                 SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
656                 SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
657                 SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
658                 SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
659 
660                 // #i22541# do not store index list for too big ranges
661                 if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
662                     ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
663 
664                 // #160184# Excel must refresh tables to make drilldown working
665                 ::set_flag( maPCInfo.mnFlags, EXC_SXDB_REFRESH_LOAD );
666 
667                 // adjust row indexes, keep one row of empty area to surely have the empty cache item
668                 if( nSrcRow1 < nDocRow1 )
669                     nSrcRow1 = nDocRow1 - 1;
670                 if( nSrcRow2 > nDocRow2 )
671                     nSrcRow2 = nDocRow2 + 1;
672 
673                 maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
674                 maDocSrcRange.aStart.SetRow( nSrcRow1 );
675                 maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
676                 maDocSrcRange.aEnd.SetRow( nSrcRow2 );
677 
678                 GetDoc().GetName( nScTab, maTabName );
679                 maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
680                 maPCInfo.mnStrmId = nListIdx + 1;
681                 maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
682 
683                 AddFields( rDPObj );
684 
685                 mbValid = true;
686             }
687         }
688     }
689 }
690 
HasItemIndexList() const691 bool XclExpPivotCache::HasItemIndexList() const
692 {
693     return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
694 }
695 
GetFieldCount() const696 sal_uInt16 XclExpPivotCache::GetFieldCount() const
697 {
698     return static_cast< sal_uInt16 >( maFieldList.GetSize() );
699 }
700 
GetField(sal_uInt16 nFieldIdx) const701 const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
702 {
703     return maFieldList.GetRecord( nFieldIdx ).get();
704 }
705 
706 //UNUSED2009-05 const XclExpPCField* XclExpPivotCache::GetField( const String& rFieldName ) const
707 //UNUSED2009-05 {
708 //UNUSED2009-05     return const_cast< XclExpPivotCache* >( this )->GetFieldAcc( rFieldName );
709 //UNUSED2009-05 }
710 
HasAddFields() const711 bool XclExpPivotCache::HasAddFields() const
712 {
713     // pivot cache can be shared, if there are no additional cache fields
714     return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
715 }
716 
HasEqualDataSource(const ScDPObject & rDPObj) const717 bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
718 {
719     /*  For now, only sheet sources are supported, therefore it is enough to
720         compare the ScSheetSourceDesc. Later, there should be done more complicated
721         comparisons regarding the source type of rDPObj and this cache. */
722     if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
723         return pSrcDesc->aSourceRange == maOrigSrcRange;
724     return false;
725 }
726 
Save(XclExpStream & rStrm)727 void XclExpPivotCache::Save( XclExpStream& rStrm )
728 {
729     DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
730     // SXIDSTM
731     XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
732     // SXVS
733     XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
734     // DCONREF
735     WriteDconref( rStrm );
736     // create the pivot cache storage stream
737     WriteCacheStream();
738 }
739 
SaveXml(XclExpXmlStream & rStrm)740 void XclExpPivotCache::SaveXml( XclExpXmlStream& rStrm )
741 {
742     DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
743     sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
744     OUString sId = OUStringBuffer()
745         .appendAscii("rId")
746         .append( rStrm.GetUniqueIdOUString() )
747         .makeStringAndClear();
748     rWorkbook->startElement( XML_pivotCache,
749             XML_cacheId, OString::valueOf( (sal_Int32)maPCInfo.mnStrmId ).getStr(),
750             FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
751             FSEND );
752     // SXIDSTM
753     XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).SaveXml( rStrm );
754     // SXVS
755     XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).SaveXml( rStrm );
756     // DCONREF
757     // OOXTODO: WriteDconref( rStrm );
758     // create the pivot cache storage stream
759     // OOXTODO: WriteCacheStream();
760     rWorkbook->endElement( XML_pivotCache );
761 }
762 
763 // private --------------------------------------------------------------------
764 
GetFieldAcc(sal_uInt16 nFieldIdx)765 XclExpPCField* XclExpPivotCache::GetFieldAcc( sal_uInt16 nFieldIdx )
766 {
767     return maFieldList.GetRecord( nFieldIdx ).get();
768 }
769 
GetFieldAcc(const String & rFieldName)770 XclExpPCField* XclExpPivotCache::GetFieldAcc( const String& rFieldName )
771 {
772     XclExpPCField* pField = 0;
773     for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
774         if( maFieldList.GetRecord( nPos )->GetFieldName() == rFieldName )
775             pField = maFieldList.GetRecord( nPos ).get();
776     return pField;
777 }
778 
AddFields(const ScDPObject & rDPObj)779 void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
780 {
781     AddStdFields( rDPObj );
782     maPCInfo.mnStdFields = GetFieldCount();
783     AddGroupFields( rDPObj );
784     AddCalcFields( rDPObj );
785     maPCInfo.mnTotalFields = GetFieldCount();
786 };
787 
AddStdFields(const ScDPObject & rDPObj)788 void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
789 {
790     // if item index list is not written, used shortened source range (maDocSrcRange) for performance
791     const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
792     // create a standard pivot cache field for each source column
793     for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
794     {
795         ScRange aColRange( rRange );
796         aColRange.aStart.SetCol( nScCol );
797         aColRange.aEnd.SetCol( nScCol );
798         maFieldList.AppendNewRecord( new XclExpPCField(
799             GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) );
800     }
801 }
802 
AddGroupFields(const ScDPObject & rDPObj)803 void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
804 {
805     if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
806     {
807         if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
808         {
809             // loop over all existing standard fields to find their group fields
810             for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
811             {
812                 if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) )
813                 {
814                     const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
815                     XclExpPCField* pLastGroupField = pCurrStdField;
816                     while( pGroupDim )
817                     {
818                         // insert the new grouping field
819                         XclExpPCFieldRef xNewGroupField( new XclExpPCField(
820                             GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
821                         maFieldList.AppendRecord( xNewGroupField );
822 
823                         // register new grouping field at current grouping field, building a chain
824                         pLastGroupField->SetGroupChildField( *xNewGroupField );
825 
826                         // next grouping dimension
827                         pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
828                         pLastGroupField = xNewGroupField.get();
829                     }
830                 }
831             }
832         }
833     }
834 }
835 
AddCalcFields(const ScDPObject &)836 void XclExpPivotCache::AddCalcFields( const ScDPObject& /*rDPObj*/ )
837 {
838     // not supported
839 }
840 
WriteDconref(XclExpStream & rStrm) const841 void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
842 {
843     XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_STRING, &maTabName ) );
844     rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
845     rStrm   << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
846             << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
847             << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
848             << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
849             << aRef
850             << sal_uInt8( 0 );
851     rStrm.EndRecord();
852 }
853 
WriteCacheStream()854 void XclExpPivotCache::WriteCacheStream()
855 {
856     SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
857     SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
858     if( xSvStrm.Is() )
859     {
860         XclExpStream aStrm( *xSvStrm, GetRoot() );
861         // SXDB
862         WriteSxdb( aStrm );
863         // SXDBEX
864         WriteSxdbex( aStrm );
865         // field list (SXFIELD and items)
866         maFieldList.Save( aStrm );
867         // index table (list of SXINDEXLIST)
868         WriteSxindexlistList( aStrm );
869         // EOF
870         XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
871     }
872 }
873 
WriteSxdb(XclExpStream & rStrm) const874 void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
875 {
876     rStrm.StartRecord( EXC_ID_SXDB, 21 );
877     rStrm << maPCInfo;
878     rStrm.EndRecord();
879 }
880 
WriteSxdbex(XclExpStream & rStrm) const881 void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm ) const
882 {
883     rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
884     rStrm   << EXC_SXDBEX_CREATION_DATE
885             << sal_uInt32( 0 );             // number of SXFORMULA records
886     rStrm.EndRecord();
887 }
888 
WriteSxindexlistList(XclExpStream & rStrm) const889 void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
890 {
891     if( HasItemIndexList() )
892     {
893         sal_Size nRecSize = 0;
894         size_t nPos, nSize = maFieldList.GetSize();
895         for( nPos = 0; nPos < nSize; ++nPos )
896             nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
897 
898         for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
899         {
900             rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
901             for( nPos = 0; nPos < nSize; ++nPos )
902                 maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
903             rStrm.EndRecord();
904         }
905     }
906 }
907 
908 // ============================================================================
909 // Pivot table
910 // ============================================================================
911 
912 namespace {
913 
914 // ----------------------------------------------------------------------------
915 
916 /** Returns a display string for a data field containing the field name and aggregation function. */
lclGetDataFieldCaption(const String & rFieldName,GeneralFunction eFunc)917 String lclGetDataFieldCaption( const String& rFieldName, GeneralFunction eFunc )
918 {
919     String aCaption;
920 
921     sal_uInt16 nResIdx = 0;
922     using namespace ::com::sun::star::sheet;
923     switch( eFunc )
924     {
925         case GeneralFunction_SUM:       nResIdx = STR_FUN_TEXT_SUM;     break;
926         case GeneralFunction_COUNT:     nResIdx = STR_FUN_TEXT_COUNT;   break;
927         case GeneralFunction_AVERAGE:   nResIdx = STR_FUN_TEXT_AVG;     break;
928         case GeneralFunction_MAX:       nResIdx = STR_FUN_TEXT_MAX;     break;
929         case GeneralFunction_MIN:       nResIdx = STR_FUN_TEXT_MIN;     break;
930         case GeneralFunction_PRODUCT:   nResIdx = STR_FUN_TEXT_PRODUCT; break;
931         case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT;   break;
932         case GeneralFunction_STDEV:     nResIdx = STR_FUN_TEXT_STDDEV;  break;
933         case GeneralFunction_STDEVP:    nResIdx = STR_FUN_TEXT_STDDEV;  break;
934         case GeneralFunction_VAR:       nResIdx = STR_FUN_TEXT_VAR;     break;
935         case GeneralFunction_VARP:      nResIdx = STR_FUN_TEXT_VAR;     break;
936         default:;
937     }
938     if( nResIdx )
939         aCaption.Assign( ScGlobal::GetRscString( nResIdx ) ).AppendAscii( RTL_CONSTASCII_STRINGPARAM( " - " ) );
940     aCaption.Append( rFieldName );
941     return aCaption;
942 }
943 
944 // ----------------------------------------------------------------------------
945 
946 } // namespace
947 
948 // ============================================================================
949 
XclExpPTItem(const XclExpPCField & rCacheField,sal_uInt16 nCacheIdx)950 XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
951     XclExpRecord( EXC_ID_SXVI, 8 ),
952     mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
953 {
954     maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
955     maItemInfo.mnCacheIdx = nCacheIdx;
956     maItemInfo.maVisName.mbUseCache = mpCacheItem != 0;
957 }
958 
XclExpPTItem(sal_uInt16 nItemType,sal_uInt16 nCacheIdx,bool bUseCache)959 XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) :
960     XclExpRecord( EXC_ID_SXVI, 8 ),
961     mpCacheItem( 0 )
962 {
963     maItemInfo.mnType = nItemType;
964     maItemInfo.mnCacheIdx = nCacheIdx;
965     maItemInfo.maVisName.mbUseCache = bUseCache;
966 }
967 
GetItemName() const968 const String& XclExpPTItem::GetItemName() const
969 {
970     return mpCacheItem ? mpCacheItem->ConvertToText() : EMPTY_STRING;
971 }
972 
SetPropertiesFromMember(const ScDPSaveMember & rSaveMem)973 void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
974 {
975     // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
976     ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
977     // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
978     ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
979 
980     // visible name
981     const OUString* pVisName = rSaveMem.GetLayoutName();
982     if (pVisName && !pVisName->equals(GetItemName()))
983         maItemInfo.SetVisName(*pVisName);
984 }
985 
WriteBody(XclExpStream & rStrm)986 void XclExpPTItem::WriteBody( XclExpStream& rStrm )
987 {
988     rStrm << maItemInfo;
989 }
990 
991 // ============================================================================
992 
XclExpPTField(const XclExpPivotTable & rPTable,sal_uInt16 nCacheIdx)993 XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
994     mrPTable( rPTable ),
995     mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
996 {
997     maFieldInfo.mnCacheIdx = nCacheIdx;
998 
999     // create field items
1000     if( mpCacheField )
1001         for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
1002             maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
1003     maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
1004 }
1005 
1006 // data access ----------------------------------------------------------------
1007 
GetFieldName() const1008 const String& XclExpPTField::GetFieldName() const
1009 {
1010     return mpCacheField ? mpCacheField->GetFieldName() : EMPTY_STRING;
1011 }
1012 
GetFieldIndex() const1013 sal_uInt16 XclExpPTField::GetFieldIndex() const
1014 {
1015     // field index always equal to cache index
1016     return maFieldInfo.mnCacheIdx;
1017 }
1018 
GetLastDataInfoIndex() const1019 sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
1020 {
1021     DBG_ASSERT( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
1022     // will return 0xFFFF for empty vector -> ok
1023     return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
1024 }
1025 
1026 //UNUSED2009-05 const XclExpPTItem* XclExpPTField::GetItem( const String& rName ) const
1027 //UNUSED2009-05 {
1028 //UNUSED2009-05     return const_cast< XclExpPTField* >( this )->GetItemAcc( rName );
1029 //UNUSED2009-05 }
1030 
GetItemIndex(const String & rName,sal_uInt16 nDefaultIdx) const1031 sal_uInt16 XclExpPTField::GetItemIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
1032 {
1033     for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
1034         if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1035             return static_cast< sal_uInt16 >( nPos );
1036     return nDefaultIdx;
1037 }
1038 
1039 // fill data --------------------------------------------------------------
1040 
1041 /**
1042  * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
1043  * are not escaped at all.
1044  */
lcl_convertCalcSubtotalName(const OUString & rName)1045 static OUString lcl_convertCalcSubtotalName(const OUString& rName)
1046 {
1047     OUStringBuffer aBuf;
1048     const sal_Unicode* p = rName.getStr();
1049     sal_Int32 n = rName.getLength();
1050     bool bEscaped = false;
1051     for (sal_Int32 i = 0; i < n; ++i)
1052     {
1053         const sal_Unicode c = p[i];
1054         if (!bEscaped && c == sal_Unicode('\\'))
1055         {
1056             bEscaped = true;
1057             continue;
1058         }
1059 
1060         aBuf.append(c);
1061         bEscaped = false;
1062     }
1063     return aBuf.makeStringAndClear();
1064 }
1065 
SetPropertiesFromDim(const ScDPSaveDimension & rSaveDim)1066 void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1067 {
1068     // orientation
1069     DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1070     DBG_ASSERT( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1071     maFieldInfo.AddApiOrient( eOrient );
1072 
1073     // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1074     ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1075 
1076     // visible name
1077     const OUString* pLayoutName = rSaveDim.GetLayoutName();
1078     if (pLayoutName && !pLayoutName->equals(GetFieldName()))
1079         maFieldInfo.SetVisName(*pLayoutName);
1080 
1081     const rtl::OUString* pSubtotalName = rSaveDim.GetSubtotalName();
1082     if (pSubtotalName)
1083     {
1084         OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1085         maFieldExtInfo.mpFieldTotalName.reset(new rtl::OUString(aSubName));
1086     }
1087 
1088     // subtotals
1089     XclPTSubtotalVec aSubtotals;
1090     aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1091     for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1092         aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1093     maFieldInfo.SetSubtotals( aSubtotals );
1094 
1095     // sorting
1096     if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1097     {
1098         maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1099         if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA )
1100             maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1101         ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1102     }
1103 
1104     // auto show
1105     if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1106     {
1107         ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1108         maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1109         maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1110         maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1111     }
1112 
1113     // layout
1114     if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1115     {
1116         maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1117         ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1118     }
1119 
1120     // special page field properties
1121     if( eOrient == DataPilotFieldOrientation_PAGE )
1122     {
1123         maPageInfo.mnField = GetFieldIndex();
1124 
1125         // selected item
1126         if( rSaveDim.HasCurrentPage() )
1127             maPageInfo.mnSelItem = GetItemIndex( rSaveDim.GetCurrentPage(), EXC_SXPI_ALLITEMS );
1128         else
1129             maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1130     }
1131 
1132     // item properties
1133     const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1134     for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; i++)
1135 		if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) )
1136 			pItem->SetPropertiesFromMember( **i );
1137 }
1138 
SetDataPropertiesFromDim(const ScDPSaveDimension & rSaveDim)1139 void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1140 {
1141     maDataInfoVec.push_back( XclPTDataFieldInfo() );
1142     XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1143     rDataInfo.mnField = GetFieldIndex();
1144 
1145     // orientation
1146     maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1147 
1148     // aggregation function
1149     GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() );
1150     rDataInfo.SetApiAggFunc( eFunc );
1151 
1152     // visible name
1153     const rtl::OUString* pVisName = rSaveDim.GetLayoutName();
1154     if (pVisName)
1155         rDataInfo.SetVisName(*pVisName);
1156     else
1157         rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1158 
1159     // result field reference
1160     if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1161     {
1162         rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1163         rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1164         if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1165         {
1166             rDataInfo.mnRefField = pRefField->GetFieldIndex();
1167             if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
1168                 rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1169         }
1170     }
1171 }
1172 
AppendSubtotalItems()1173 void XclExpPTField::AppendSubtotalItems()
1174 {
1175     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT )   AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1176     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM )       AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1177     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT )     AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1178     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE )   AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1179     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX )       AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1180     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN )       AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1181     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD )      AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1182     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM )  AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1183     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV )    AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1184     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP )   AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1185     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR )       AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1186     if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP )      AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1187 }
1188 
1189 // records --------------------------------------------------------------------
1190 
WriteSxpiEntry(XclExpStream & rStrm) const1191 void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1192 {
1193     rStrm << maPageInfo;
1194 }
1195 
WriteSxdi(XclExpStream & rStrm,sal_uInt16 nDataInfoIdx) const1196 void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1197 {
1198     DBG_ASSERT( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1199     if( nDataInfoIdx < maDataInfoVec.size() )
1200     {
1201         rStrm.StartRecord( EXC_ID_SXDI, 12 );
1202         rStrm << maDataInfoVec[ nDataInfoIdx ];
1203         rStrm.EndRecord();
1204     }
1205 }
1206 
Save(XclExpStream & rStrm)1207 void XclExpPTField::Save( XclExpStream& rStrm )
1208 {
1209     // SXVD
1210     WriteSxvd( rStrm );
1211     // list of SXVI records
1212     maItemList.Save( rStrm );
1213     // SXVDEX
1214     WriteSxvdex( rStrm );
1215 }
1216 
1217 // private --------------------------------------------------------------------
1218 
GetItemAcc(const String & rName)1219 XclExpPTItem* XclExpPTField::GetItemAcc( const String& rName )
1220 {
1221     XclExpPTItem* pItem = 0;
1222     for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1223         if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1224             pItem = maItemList.GetRecord( nPos ).get();
1225     return pItem;
1226 }
1227 
AppendSubtotalItem(sal_uInt16 nItemType)1228 void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1229 {
1230     maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) );
1231     ++maFieldInfo.mnItemCount;
1232 }
1233 
WriteSxvd(XclExpStream & rStrm) const1234 void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1235 {
1236     rStrm.StartRecord( EXC_ID_SXVD, 10 );
1237     rStrm << maFieldInfo;
1238     rStrm.EndRecord();
1239 }
1240 
WriteSxvdex(XclExpStream & rStrm) const1241 void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1242 {
1243     rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1244     rStrm << maFieldExtInfo;
1245     rStrm.EndRecord();
1246 }
1247 
1248 // ============================================================================
1249 
XclExpPivotTable(const XclExpRoot & rRoot,const ScDPObject & rDPObj,const XclExpPivotCache & rPCache)1250 XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
1251     XclExpRoot( rRoot ),
1252     mrPCache( rPCache ),
1253     maDataOrientField( *this, EXC_SXIVD_DATA ),
1254     mnOutScTab( 0 ),
1255     mbValid( false ),
1256     mbFilterBtn( false )
1257 {
1258     const ScRange& rOutScRange = rDPObj.GetOutRange();
1259     if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1260     {
1261         // DataPilot properties -----------------------------------------------
1262 
1263         // pivot table properties from DP object
1264         mnOutScTab = rOutScRange.aStart.Tab();
1265         maPTInfo.maTableName = rDPObj.GetName();
1266         maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1267 
1268         maPTViewEx9Info.Init( rDPObj );
1269 
1270         if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1271         {
1272             // additional properties from ScDPSaveData
1273             SetPropertiesFromDP( *pSaveData );
1274 
1275             // loop over all dimensions ---------------------------------------
1276 
1277             /*  1)  Default-construct all pivot table fields for all pivot cache fields. */
1278             for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1279                 maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1280 
1281             const List& rDimList = pSaveData->GetDimensions();
1282             sal_uLong nDimIdx, nDimCount = rDimList.Count();
1283 
1284             /*  2)  First process all data dimensions, they are needed for extended
1285                     settings of row/column/page fields (sorting/auto show). */
1286             for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
1287                 if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
1288                     if( pSaveDim->GetOrientation() == DataPilotFieldOrientation_DATA )
1289                         SetDataFieldPropertiesFromDim( *pSaveDim );
1290 
1291             /*  3)  Row/column/page/hidden fields. */
1292             for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
1293                 if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
1294                     if( pSaveDim->GetOrientation() != DataPilotFieldOrientation_DATA )
1295                         SetFieldPropertiesFromDim( *pSaveDim );
1296 
1297             // Finalize -------------------------------------------------------
1298 
1299             Finalize();
1300             mbValid = true;
1301         }
1302     }
1303 }
1304 
GetCacheField(sal_uInt16 nCacheIdx) const1305 const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1306 {
1307     return mrPCache.GetField( nCacheIdx );
1308 }
1309 
GetField(sal_uInt16 nFieldIdx) const1310 const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1311 {
1312     return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
1313 }
1314 
GetField(const String & rName) const1315 const XclExpPTField* XclExpPivotTable::GetField( const String& rName ) const
1316 {
1317     return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1318 }
1319 
GetDataFieldIndex(const String & rName,sal_uInt16 nDefaultIdx) const1320 sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
1321 {
1322     for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1323         if( const XclExpPTField* pField = GetField( aIt->first ) )
1324             if( pField->GetFieldName() == rName )
1325                 return static_cast< sal_uInt16 >( aIt - maDataFields.begin() );
1326     return nDefaultIdx;
1327 }
1328 
Save(XclExpStream & rStrm)1329 void XclExpPivotTable::Save( XclExpStream& rStrm )
1330 {
1331     if( mbValid )
1332     {
1333         // SXVIEW
1334         WriteSxview( rStrm );
1335         // pivot table fields (SXVD, SXVDEX, and item records)
1336         maFieldList.Save( rStrm );
1337         // SXIVD records for row and column fields
1338         WriteSxivd( rStrm, maRowFields );
1339         WriteSxivd( rStrm, maColFields );
1340         // SXPI
1341         WriteSxpi( rStrm );
1342         // list of SXDI records containing data field info
1343         WriteSxdiList( rStrm );
1344         // SXLI records
1345         WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1346         WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1347         // SXEX
1348         WriteSxex( rStrm );
1349         // QSISXTAG
1350         WriteQsiSxTag( rStrm );
1351         // SXVIEWEX9
1352         WriteSxViewEx9( rStrm );
1353     }
1354 }
1355 
1356 // private --------------------------------------------------------------------
1357 
GetFieldAcc(const String & rName)1358 XclExpPTField* XclExpPivotTable::GetFieldAcc( const String& rName )
1359 {
1360     XclExpPTField* pField = 0;
1361     for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1362         if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1363             pField = maFieldList.GetRecord( nPos ).get();
1364     return pField;
1365 }
1366 
GetFieldAcc(const ScDPSaveDimension & rSaveDim)1367 XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1368 {
1369     // data field orientation field?
1370     if( rSaveDim.IsDataLayout() )
1371         return &maDataOrientField;
1372 
1373     // a real dimension
1374     String aFieldName( rSaveDim.GetName() );
1375     return aFieldName.Len() ? GetFieldAcc( aFieldName ) : 0;
1376 }
1377 
1378 // fill data --------------------------------------------------------------
1379 
SetPropertiesFromDP(const ScDPSaveData & rSaveData)1380 void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1381 {
1382     ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1383     ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1384     ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1385     mbFilterBtn = rSaveData.GetFilterButton();
1386     const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1387     if (!pDim)
1388         return;
1389 
1390     const rtl::OUString* pLayoutName = pDim->GetLayoutName();
1391     if (pLayoutName)
1392         maPTInfo.maDataName = *pLayoutName;
1393     else
1394         maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
1395 }
1396 
SetFieldPropertiesFromDim(const ScDPSaveDimension & rSaveDim)1397 void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1398 {
1399     if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1400     {
1401         // field properties
1402         pField->SetPropertiesFromDim( rSaveDim );
1403 
1404         // update the corresponding field position list
1405         DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1406         sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1407         bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1408         bool bMultiData = maDataFields.size() > 1;
1409 
1410         if( !bDataLayout || bMultiData ) switch( eOrient )
1411         {
1412             case DataPilotFieldOrientation_ROW:
1413                 maRowFields.push_back( nFieldIdx );
1414                 if( bDataLayout )
1415                     maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1416             break;
1417             case DataPilotFieldOrientation_COLUMN:
1418                 maColFields.push_back( nFieldIdx );
1419                 if( bDataLayout )
1420                     maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1421             break;
1422             case DataPilotFieldOrientation_PAGE:
1423                 maPageFields.push_back( nFieldIdx );
1424                 DBG_ASSERT( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1425             break;
1426             case DataPilotFieldOrientation_DATA:
1427                 DBG_ERRORFILE( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1428             break;
1429             default:;
1430         }
1431     }
1432 }
1433 
SetDataFieldPropertiesFromDim(const ScDPSaveDimension & rSaveDim)1434 void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1435 {
1436     if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1437     {
1438         // field properties
1439         pField->SetDataPropertiesFromDim( rSaveDim );
1440         // update the data field position list
1441         maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) );
1442     }
1443 }
1444 
Finalize()1445 void XclExpPivotTable::Finalize()
1446 {
1447     // field numbers
1448     maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1449     maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1450     maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1451     maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1452     maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1453 
1454     maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1455     maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1456 
1457     // subtotal items
1458     for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1459         maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1460 
1461     // find data field orientation field
1462     maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1463     const ScfUInt16Vec* pFieldVec = 0;
1464     switch( maPTInfo.mnDataAxis )
1465     {
1466         case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields;   break;
1467         case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields;   break;
1468     }
1469 
1470     if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1471     {
1472         ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1473         if( aIt != pFieldVec->end() )
1474             maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() );
1475     }
1476 
1477     // single data field is always row oriented
1478     if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1479         maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1480 
1481     // update output range (initialized in ctor)
1482     sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1483     sal_uInt16& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1484     sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1485     sal_uInt16& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1486     // exclude page fields from output range
1487     rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1488     // exclude filter button from output range
1489     if( mbFilterBtn )
1490         ++rnXclRow1;
1491     // exclude empty row between (filter button and/or page fields) and table
1492     if( mbFilterBtn || maPTInfo.mnPageFields )
1493         ++rnXclRow1;
1494 
1495     // data area
1496     sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1497     sal_uInt16& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1498     rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1499     rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1500     if( maDataFields.empty() )
1501         ++rnDataXclRow;
1502 
1503     bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1504     if (bExtraHeaderRow)
1505         // Insert an extra row only when there is no column field.
1506         ++rnDataXclRow;
1507 
1508     rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1509     rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1510     maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1511     maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1512 
1513     // first heading
1514     maPTInfo.mnFirstHeadRow = rnXclRow1;
1515     if (bExtraHeaderRow)
1516         maPTInfo.mnFirstHeadRow += 2;
1517 }
1518 
1519 // records ----------------------------------------------------------------
1520 
WriteSxview(XclExpStream & rStrm) const1521 void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1522 {
1523     rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.Len() + maPTInfo.maDataName.Len() );
1524     rStrm << maPTInfo;
1525     rStrm.EndRecord();
1526 }
1527 
WriteSxivd(XclExpStream & rStrm,const ScfUInt16Vec & rFields) const1528 void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields ) const
1529 {
1530     if( !rFields.empty() )
1531     {
1532         rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1533         for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt )
1534             rStrm << *aIt;
1535         rStrm.EndRecord();
1536     }
1537 }
1538 
WriteSxpi(XclExpStream & rStrm) const1539 void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1540 {
1541     if( !maPageFields.empty() )
1542     {
1543         rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1544         rStrm.SetSliceSize( 6 );
1545         for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
1546         {
1547             XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt );
1548             if( xField.is() )
1549                 xField->WriteSxpiEntry( rStrm );
1550         }
1551         rStrm.EndRecord();
1552     }
1553 }
1554 
WriteSxdiList(XclExpStream & rStrm) const1555 void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1556 {
1557     for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1558     {
1559         XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first );
1560         if( xField.is() )
1561             xField->WriteSxdi( rStrm, aIt->second );
1562     }
1563 }
1564 
WriteSxli(XclExpStream & rStrm,sal_uInt16 nLineCount,sal_uInt16 nIndexCount) const1565 void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const
1566 {
1567     if( nLineCount > 0 )
1568     {
1569         sal_uInt16 nLineSize = 8 + 2 * nIndexCount;
1570         rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1571 
1572         /*  #158444# Excel expects the records to be filled completely, do not
1573             set a segment size... */
1574 //        rStrm.SetSliceSize( nLineSize );
1575 
1576         for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1577         {
1578             // #106598# Excel XP needs a partly initialized SXLI record
1579             rStrm   << sal_uInt16( 0 )      // number of equal index entries
1580                     << EXC_SXVI_TYPE_DATA
1581                     << nIndexCount
1582                     << EXC_SXLI_DEFAULTFLAGS;
1583             rStrm.WriteZeroBytes( 2 * nIndexCount );
1584         }
1585         rStrm.EndRecord();
1586     }
1587 }
1588 
WriteSxex(XclExpStream & rStrm) const1589 void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1590 {
1591     rStrm.StartRecord( EXC_ID_SXEX, 24 );
1592     rStrm << maPTExtInfo;
1593     rStrm.EndRecord();
1594 }
1595 
WriteQsiSxTag(XclExpStream & rStrm) const1596 void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1597 {
1598     rStrm.StartRecord( 0x0802, 32 );
1599 
1600     sal_uInt16 nRecordType = 0x0802;
1601     sal_uInt16 nDummyFlags = 0x0000;
1602     sal_uInt16 nTableType  = 1; // 0 = query table : 1 = pivot table
1603 
1604     rStrm << nRecordType << nDummyFlags << nTableType;
1605 
1606     // General flags
1607     bool bEnableRefresh = true;
1608     bool bPCacheInvalid = false;
1609     bool bOlapPTReport  = false;
1610 
1611     sal_uInt16 nFlags = 0x0000;
1612     if (bEnableRefresh) nFlags |= 0x0001;
1613     if (bPCacheInvalid) nFlags |= 0x0002;
1614     if (bOlapPTReport)  nFlags |= 0x0004;
1615     rStrm << nFlags;
1616 
1617     // Feature-specific options.  The value differs depending on the table
1618     // type, but we assume the table type is always pivot table.
1619     sal_uInt32 nOptions = 0x00000000;
1620     bool bNoStencil = false;
1621     bool bHideTotal = false;
1622     bool bEmptyRows = false;
1623     bool bEmptyCols = false;
1624     if (bNoStencil) nOptions |= 0x00000001;
1625     if (bHideTotal) nOptions |= 0x00000002;
1626     if (bEmptyRows) nOptions |= 0x00000008;
1627     if (bEmptyCols) nOptions |= 0x00000010;
1628     rStrm << nOptions;
1629 
1630     enum ExcelVersion
1631     {
1632         Excel2000 = 0,
1633         ExcelXP   = 1,
1634         Excel2003 = 2,
1635         Excel2007 = 3
1636     };
1637     ExcelVersion eXclVer = Excel2000;
1638     sal_uInt8 nOffsetBytes = 16;
1639     rStrm << static_cast<sal_uInt8>(eXclVer)  // version table last refreshed
1640           << static_cast<sal_uInt8>(eXclVer)  // minimum version to refresh
1641           << nOffsetBytes
1642           << static_cast<sal_uInt8>(eXclVer); // first version created
1643 
1644     rStrm << XclExpString(maPTInfo.maTableName);
1645     rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1646 
1647     rStrm.EndRecord();
1648 }
1649 
WriteSxViewEx9(XclExpStream & rStrm) const1650 void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1651 {
1652     // Until we sync the autoformat ids only export if using grid header layout
1653     // That could only have been set via xls import so far.
1654     if ( 0 == maPTViewEx9Info.mnGridLayout )
1655     {
1656         rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1657         rStrm << maPTViewEx9Info;
1658         rStrm.EndRecord();
1659     }
1660 }
1661 
1662 // ============================================================================
1663 
1664 namespace {
1665 
1666 const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1667 
1668 /** Record wrapper class to write the pivot caches or pivot tables. */
1669 class XclExpPivotRecWrapper : public XclExpRecordBase
1670 {
1671 public:
1672     explicit            XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1673     virtual void        Save( XclExpStream& rStrm );
1674     virtual void        SaveXml( XclExpXmlStream& rStrm );
1675 private:
1676     XclExpPivotTableManager& mrPTMgr;
1677     SCTAB               mnScTab;
1678 };
1679 
XclExpPivotRecWrapper(XclExpPivotTableManager & rPTMgr,SCTAB nScTab)1680 XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1681     mrPTMgr( rPTMgr ),
1682     mnScTab( nScTab )
1683 {
1684 }
1685 
Save(XclExpStream & rStrm)1686 void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1687 {
1688     if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1689         mrPTMgr.WritePivotCaches( rStrm );
1690     else
1691         mrPTMgr.WritePivotTables( rStrm, mnScTab );
1692 }
1693 
SaveXml(XclExpXmlStream & rStrm)1694 void XclExpPivotRecWrapper::SaveXml( XclExpXmlStream& rStrm )
1695 {
1696     if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1697         mrPTMgr.WritePivotCachesXml( rStrm );
1698     else
1699         mrPTMgr.WritePivotTablesXml( rStrm, mnScTab );
1700 }
1701 
1702 } // namespace
1703 
1704 // ----------------------------------------------------------------------------
1705 
XclExpPivotTableManager(const XclExpRoot & rRoot)1706 XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1707     XclExpRoot( rRoot ),
1708     mbShareCaches( true )
1709 {
1710 }
1711 
CreatePivotTables()1712 void XclExpPivotTableManager::CreatePivotTables()
1713 {
1714     if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1715         for( sal_uInt16 nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1716             if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] )
1717                 if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) )
1718                     maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache ) );
1719 }
1720 
CreatePivotCachesRecord()1721 XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1722 {
1723     return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
1724 }
1725 
CreatePivotTablesRecord(SCTAB nScTab)1726 XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1727 {
1728     return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
1729 }
1730 
WritePivotCaches(XclExpStream & rStrm)1731 void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1732 {
1733     maPCacheList.Save( rStrm );
1734 }
1735 
WritePivotCachesXml(XclExpXmlStream & rStrm)1736 void XclExpPivotTableManager::WritePivotCachesXml( XclExpXmlStream& rStrm )
1737 {
1738     if( maPCacheList.IsEmpty() )
1739         return;
1740     sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
1741     rWorkbook->startElement( XML_pivotCaches, FSEND );
1742     maPCacheList.SaveXml( rStrm );
1743     rWorkbook->endElement( XML_pivotCaches );
1744 }
1745 
WritePivotTables(XclExpStream & rStrm,SCTAB nScTab)1746 void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1747 {
1748     for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1749     {
1750         XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1751         if( xPTable->GetScTab() == nScTab )
1752             xPTable->Save( rStrm );
1753     }
1754 }
1755 
WritePivotTablesXml(XclExpXmlStream & rStrm,SCTAB nScTab)1756 void XclExpPivotTableManager::WritePivotTablesXml( XclExpXmlStream& rStrm, SCTAB nScTab )
1757 {
1758     for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1759     {
1760         XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1761         if( xPTable->GetScTab() == nScTab )
1762             xPTable->SaveXml( rStrm );
1763     }
1764 }
1765 
1766 // private --------------------------------------------------------------------
1767 
CreatePivotCache(const ScDPObject & rDPObj)1768 const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1769 {
1770     // try to find a pivot cache with the same data source
1771     /*  #i25110# In Excel, the pivot cache contains additional fields
1772         (i.e. grouping info, calculated fields). If the passed DataPilot object
1773         or the found cache contains this data, do not share the cache with
1774         multiple pivot tables. */
1775     if( mbShareCaches )
1776     {
1777         if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1778         {
1779             const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1780             // no dimension save data at all or save data does not contain grouping info
1781             if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1782             {
1783                 // check all existing pivot caches
1784                 for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1785                 {
1786                     XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
1787                     // pivot cache does not have grouping info and source data is equal
1788                     if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
1789                         return xPCache.get();
1790                 }
1791             }
1792         }
1793     }
1794 
1795     // create a new pivot cache
1796     sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1797     XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
1798     if( xNewPCache->IsValid() )
1799     {
1800         maPCacheList.AppendRecord( xNewPCache );
1801         return xNewPCache.get();
1802     }
1803 
1804     return 0;
1805 }
1806 
1807 // ============================================================================
1808 
1809