1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: pivotcachefragment.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "oox/xls/pivotcachefragment.hxx"
32 #include "oox/helper/attributelist.hxx"
33 #include "oox/helper/recordinputstream.hxx"
34 #include "oox/xls/addressconverter.hxx"
35 #include "oox/xls/biffinputstream.hxx"
36 #include "oox/xls/pivotcachebuffer.hxx"
38 using ::rtl::OUString
;
39 using ::com::sun::star::uno::Any
;
40 using ::oox::core::ContextHandlerRef
;
41 using ::oox::core::RecordInfo
;
46 // ============================================================================
48 OoxPivotCacheFieldContext::OoxPivotCacheFieldContext( OoxWorkbookFragmentBase
& rFragment
, PivotCacheField
& rCacheField
) :
49 OoxWorkbookContextBase( rFragment
),
50 mrCacheField( rCacheField
)
54 ContextHandlerRef
OoxPivotCacheFieldContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
56 switch( getCurrentElement() )
58 case XLS_TOKEN( cacheField
):
59 if( nElement
== XLS_TOKEN( sharedItems
) ) { mrCacheField
.importSharedItems( rAttribs
); return this; }
60 if( nElement
== XLS_TOKEN( fieldGroup
) ) { mrCacheField
.importFieldGroup( rAttribs
); return this; }
63 case XLS_TOKEN( fieldGroup
):
66 case XLS_TOKEN( rangePr
): mrCacheField
.importRangePr( rAttribs
); break;
67 case XLS_TOKEN( discretePr
): return this;
68 case XLS_TOKEN( groupItems
): return this;
72 case XLS_TOKEN( sharedItems
): mrCacheField
.importSharedItem( nElement
, rAttribs
); break;
73 case XLS_TOKEN( discretePr
): mrCacheField
.importDiscretePrItem( nElement
, rAttribs
); break;
74 case XLS_TOKEN( groupItems
): mrCacheField
.importGroupItem( nElement
, rAttribs
); break;
79 void OoxPivotCacheFieldContext::onStartElement( const AttributeList
& rAttribs
)
82 mrCacheField
.importCacheField( rAttribs
);
85 ContextHandlerRef
OoxPivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId
, RecordInputStream
& rStrm
)
87 switch( getCurrentElement() )
89 case OOBIN_ID_PCDFIELD
:
92 case OOBIN_ID_PCDFSHAREDITEMS
: mrCacheField
.importPCDFSharedItems( rStrm
); return this;
93 case OOBIN_ID_PCDFIELDGROUP
: mrCacheField
.importPCDFieldGroup( rStrm
); return this;
97 case OOBIN_ID_PCDFIELDGROUP
:
100 case OOBIN_ID_PCDFRANGEPR
: mrCacheField
.importPCDFRangePr( rStrm
); break;
101 case OOBIN_ID_PCDFDISCRETEPR
: return this;
102 case OOBIN_ID_PCDFGROUPITEMS
: return this;
106 case OOBIN_ID_PCDFSHAREDITEMS
: mrCacheField
.importPCDFSharedItem( nRecId
, rStrm
); break;
107 case OOBIN_ID_PCDFDISCRETEPR
: mrCacheField
.importPCDFDiscretePrItem( nRecId
, rStrm
); break;
108 case OOBIN_ID_PCDFGROUPITEMS
: mrCacheField
.importPCDFGroupItem( nRecId
, rStrm
); break;
113 void OoxPivotCacheFieldContext::onStartRecord( RecordInputStream
& rStrm
)
115 if( isRootElement() )
116 mrCacheField
.importPCDField( rStrm
);
119 // ============================================================================
121 OoxPivotCacheDefinitionFragment::OoxPivotCacheDefinitionFragment(
122 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
, PivotCache
& rPivotCache
) :
123 OoxWorkbookFragmentBase( rHelper
, rFragmentPath
),
124 mrPivotCache( rPivotCache
)
128 ContextHandlerRef
OoxPivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
130 switch( getCurrentElement() )
132 case XML_ROOT_CONTEXT
:
133 if( nElement
== XLS_TOKEN( pivotCacheDefinition
) ) { mrPivotCache
.importPivotCacheDefinition( rAttribs
); return this; }
136 case XLS_TOKEN( pivotCacheDefinition
):
139 case XLS_TOKEN( cacheSource
): mrPivotCache
.importCacheSource( rAttribs
); return this;
140 case XLS_TOKEN( cacheFields
): return this;
144 case XLS_TOKEN( cacheSource
):
145 if( nElement
== XLS_TOKEN( worksheetSource
) ) mrPivotCache
.importWorksheetSource( rAttribs
, getRelations() );
148 case XLS_TOKEN( cacheFields
):
149 if( nElement
== XLS_TOKEN( cacheField
) ) return new OoxPivotCacheFieldContext( *this, mrPivotCache
.createCacheField() );
155 ContextHandlerRef
OoxPivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId
, RecordInputStream
& rStrm
)
157 switch( getCurrentElement() )
159 case XML_ROOT_CONTEXT
:
160 if( nRecId
== OOBIN_ID_PCDEFINITION
) { mrPivotCache
.importPCDefinition( rStrm
); return this; }
163 case OOBIN_ID_PCDEFINITION
:
166 case OOBIN_ID_PCDSOURCE
: mrPivotCache
.importPCDSource( rStrm
); return this;
167 case OOBIN_ID_PCDFIELDS
: return this;
171 case OOBIN_ID_PCDSOURCE
:
172 if( nRecId
== OOBIN_ID_PCDSHEETSOURCE
) mrPivotCache
.importPCDSheetSource( rStrm
, getRelations() );
175 case OOBIN_ID_PCDFIELDS
:
176 if( nRecId
== OOBIN_ID_PCDFIELD
) return new OoxPivotCacheFieldContext( *this, mrPivotCache
.createCacheField() );
182 const RecordInfo
* OoxPivotCacheDefinitionFragment::getRecordInfos() const
184 static const RecordInfo spRecInfos
[] =
186 { OOBIN_ID_PCDEFINITION
, OOBIN_ID_PCDEFINITION
+ 1 },
187 { OOBIN_ID_PCDFDISCRETEPR
, OOBIN_ID_PCDFDISCRETEPR
+ 1 },
188 { OOBIN_ID_PCDFGROUPITEMS
, OOBIN_ID_PCDFGROUPITEMS
+ 1 },
189 { OOBIN_ID_PCDFIELD
, OOBIN_ID_PCDFIELD
+ 1 },
190 { OOBIN_ID_PCDFIELDGROUP
, OOBIN_ID_PCDFIELDGROUP
+ 1 },
191 { OOBIN_ID_PCDFIELDS
, OOBIN_ID_PCDFIELDS
+ 1 },
192 { OOBIN_ID_PCDFRANGEPR
, OOBIN_ID_PCDFRANGEPR
+ 1 },
193 { OOBIN_ID_PCDFSHAREDITEMS
, OOBIN_ID_PCDFSHAREDITEMS
+ 1 },
194 { OOBIN_ID_PCITEM_ARRAY
, OOBIN_ID_PCITEM_ARRAY
+ 1 },
195 { OOBIN_ID_PCDSHEETSOURCE
, OOBIN_ID_PCDSHEETSOURCE
+ 1 },
196 { OOBIN_ID_PCDSOURCE
, OOBIN_ID_PCDSOURCE
+ 1 },
202 void OoxPivotCacheDefinitionFragment::finalizeImport()
204 // finalize the cache (check source range etc.)
205 mrPivotCache
.finalizeImport();
207 // load the cache records, if the cache is based on a deleted or an external worksheet
208 if( mrPivotCache
.isValidDataSource() && mrPivotCache
.isBasedOnDummySheet() )
210 OUString aRecFragmentPath
= getRelations().getFragmentPathFromRelId( mrPivotCache
.getRecordsRelId() );
211 if( aRecFragmentPath
.getLength() > 0 )
212 importOoxFragment( new OoxPivotCacheRecordsFragment( *this, aRecFragmentPath
, mrPivotCache
) );
216 // ============================================================================
218 OoxPivotCacheRecordsFragment::OoxPivotCacheRecordsFragment( const WorkbookHelper
& rHelper
,
219 const OUString
& rFragmentPath
, const PivotCache
& rPivotCache
) :
220 OoxWorksheetFragmentBase( rHelper
, rFragmentPath
, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET
, rPivotCache
.getSourceRange().Sheet
),
221 mrPivotCache( rPivotCache
),
226 // prepare sheet: insert column header names into top row
227 rPivotCache
.writeSourceHeaderCells( *this );
230 ContextHandlerRef
OoxPivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
232 switch( getCurrentElement() )
234 case XML_ROOT_CONTEXT
:
235 if( nElement
== XLS_TOKEN( pivotCacheRecords
) ) return this;
238 case XLS_TOKEN( pivotCacheRecords
):
239 if( nElement
== XLS_TOKEN( r
) ) { startCacheRecord(); return this; }
244 PivotCacheItem aItem
;
247 case XLS_TOKEN( m
): break;
248 case XLS_TOKEN( s
): aItem
.readString( rAttribs
); break;
249 case XLS_TOKEN( n
): aItem
.readNumeric( rAttribs
); break;
250 case XLS_TOKEN( d
): aItem
.readDate( rAttribs
); break;
251 case XLS_TOKEN( b
): aItem
.readBool( rAttribs
); break;
252 case XLS_TOKEN( e
): aItem
.readError( rAttribs
, getUnitConverter() ); break;
253 case XLS_TOKEN( x
): aItem
.readIndex( rAttribs
); break;
254 default: OSL_ENSURE( false, "OoxPivotCacheRecordsFragment::onCreateContext - unexpected element" );
256 mrPivotCache
.writeSourceDataCell( *this, mnCol
, mnRow
, aItem
);
264 ContextHandlerRef
OoxPivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId
, RecordInputStream
& rStrm
)
266 switch( getCurrentElement() )
268 case XML_ROOT_CONTEXT
:
269 if( nRecId
== OOBIN_ID_PCRECORDS
) return this;
272 case OOBIN_ID_PCRECORDS
:
275 case OOBIN_ID_PCRECORD
: importPCRecord( rStrm
); break;
276 case OOBIN_ID_PCRECORDDT
: startCacheRecord(); break;
277 default: importPCRecordItem( nRecId
, rStrm
); break;
284 const RecordInfo
* OoxPivotCacheRecordsFragment::getRecordInfos() const
286 static const RecordInfo spRecInfos
[] =
288 { OOBIN_ID_PCRECORDS
, OOBIN_ID_PCRECORDS
+ 1 },
294 // private --------------------------------------------------------------------
296 void OoxPivotCacheRecordsFragment::startCacheRecord()
303 void OoxPivotCacheRecordsFragment::importPCRecord( RecordInputStream
& rStrm
)
306 mrPivotCache
.importPCRecord( rStrm
, *this, mnRow
);
310 void OoxPivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId
, RecordInputStream
& rStrm
)
314 PivotCacheItem aItem
;
317 case OOBIN_ID_PCITEM_MISSING
: break;
318 case OOBIN_ID_PCITEM_STRING
: aItem
.readString( rStrm
); break;
319 case OOBIN_ID_PCITEM_DOUBLE
: aItem
.readDouble( rStrm
); break;
320 case OOBIN_ID_PCITEM_DATE
: aItem
.readDate( rStrm
); break;
321 case OOBIN_ID_PCITEM_BOOL
: aItem
.readBool( rStrm
); break;
322 case OOBIN_ID_PCITEM_ERROR
: aItem
.readError( rStrm
); break;
323 case OOBIN_ID_PCITEM_INDEX
: aItem
.readIndex( rStrm
); break;
324 default: OSL_ENSURE( false, "OoxPivotCacheRecordsFragment::importPCRecordItem - unexpected record" );
326 mrPivotCache
.writeSourceDataCell( *this, mnCol
, mnRow
, aItem
);
331 // ============================================================================
332 // ============================================================================
336 bool lclSeekToPCDField( BiffInputStream
& rStrm
)
338 sal_Int64 nRecHandle
= rStrm
.getRecHandle();
339 while( rStrm
.startNextRecord() )
340 if( rStrm
.getRecId() == BIFF_ID_PCDFIELD
)
342 rStrm
.startRecordByHandle( nRecHandle
);
348 // ----------------------------------------------------------------------------
350 BiffPivotCacheFragment::BiffPivotCacheFragment(
351 const WorkbookHelper
& rHelper
, const ::rtl::OUString
& rStrmName
, PivotCache
& rPivotCache
) :
352 BiffWorkbookFragmentBase( rHelper
, rStrmName
, true ),
353 mrPivotCache( rPivotCache
)
357 bool BiffPivotCacheFragment::importFragment()
359 if( mrStrm
.startNextRecord() && (mrStrm
.getRecId() == BIFF_ID_PCDEFINITION
) )
361 // read PCDEFINITION and optional PCDEFINITION2 records
362 mrPivotCache
.importPCDefinition( mrStrm
);
364 // read cache fields as long as another PCDFIELD record can be found
365 while( lclSeekToPCDField( mrStrm
) )
366 mrPivotCache
.createCacheField( true ).importPCDField( mrStrm
);
368 // finalize the cache (check source range etc.)
369 mrPivotCache
.finalizeImport();
371 // load the cache records, if the cache is based on a deleted or an external worksheet
372 if( mrPivotCache
.isValidDataSource() && mrPivotCache
.isBasedOnDummySheet() )
374 /* Last call of lclSeekToPCDField() failed and kept stream position
375 unchanged. Stream should point to source data table now. */
376 BiffPivotCacheRecordsContext
aContext( *this, mrPivotCache
);
377 if( aContext
.isValidSheet() )
378 while( mrStrm
.startNextRecord() && (mrStrm
.getRecId() != BIFF_ID_EOF
) )
379 aContext
.importRecord();
383 return mrStrm
.getRecId() == BIFF_ID_EOF
;
386 // ============================================================================
388 BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext(
389 const BiffWorkbookFragmentBase
& rFragment
, const PivotCache
& rPivotCache
) :
390 BiffWorksheetContextBase( rFragment
, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET
, rPivotCache
.getSourceRange().Sheet
),
391 mrPivotCache( rPivotCache
),
394 mbHasShared( false ),
397 // prepare sheet: insert column header names into top row
398 mrPivotCache
.writeSourceHeaderCells( *this );
400 // find all fields without shared items, remember column indexes in source data
401 for( sal_Int32 nFieldIdx
= 0, nFieldCount
= mrPivotCache
.getCacheFieldCount(), nCol
= 0; nFieldIdx
< nFieldCount
; ++nFieldIdx
)
403 const PivotCacheField
* pCacheField
= mrPivotCache
.getCacheField( nFieldIdx
);
404 if( pCacheField
&& pCacheField
->isDatabaseField() )
406 if( pCacheField
->hasSharedItems() )
409 maUnsharedCols
.push_back( nCol
);
415 void BiffPivotCacheRecordsContext::importRecord()
417 if( mrStrm
.getRecId() == BIFF_ID_PCITEM_INDEXLIST
)
419 OSL_ENSURE( mbHasShared
, "BiffPivotCacheRecordsContext::importRecord - unexpected PCITEM_INDEXLIST record" );
420 // PCITEM_INDEXLIST record always in front of a new data row
422 mrPivotCache
.importPCItemIndexList( mrStrm
, *this, mnRow
);
423 mbInRow
= !maUnsharedCols
.empty(); // mbInRow remains true, if unshared items are expected
427 PivotCacheItem aItem
;
428 switch( mrStrm
.getRecId() )
430 case BIFF_ID_PCITEM_MISSING
: break;
431 case BIFF_ID_PCITEM_STRING
: aItem
.readString( mrStrm
, *this ); break;
432 case BIFF_ID_PCITEM_DOUBLE
: aItem
.readDouble( mrStrm
); break;
433 case BIFF_ID_PCITEM_INTEGER
: aItem
.readInteger( mrStrm
); break;
434 case BIFF_ID_PCITEM_DATE
: aItem
.readDate( mrStrm
); break;
435 case BIFF_ID_PCITEM_BOOL
: aItem
.readBool( mrStrm
); break;
436 case BIFF_ID_PCITEM_ERROR
: aItem
.readError( mrStrm
); break;
437 default: return; // unknown record, ignore
440 // find next column index, might start new row if no fields with shared items exist
441 if( mbInRow
&& (mnColIdx
== maUnsharedCols
.size()) )
443 OSL_ENSURE( !mbHasShared
, "BiffPivotCacheRecordsContext::importRecord - PCITEM_INDEXLIST record missing" );
444 mbInRow
= mbHasShared
; // do not leave current row if PCITEM_INDEXLIST is expected
446 // start next row on first call, or on row wrap without shared items
450 // write the item data to the sheet cell
451 OSL_ENSURE( mnColIdx
< maUnsharedCols
.size(), "BiffPivotCacheRecordsContext::importRecord - invalid column index" );
452 if( mnColIdx
< maUnsharedCols
.size() )
453 mrPivotCache
.writeSourceDataCell( *this, maUnsharedCols
[ mnColIdx
], mnRow
, aItem
);
457 void BiffPivotCacheRecordsContext::startNextRow()
464 // ============================================================================