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