1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "pivotcachefragment.hxx"
22 #include <osl/diagnose.h>
23 #include <oox/helper/attributelist.hxx>
24 #include "addressconverter.hxx"
25 #include "biffinputstream.hxx"
26 #include "formulabuffer.hxx"
27 #include "pivotcachebuffer.hxx"
28 #include "worksheetbuffer.hxx"
33 using namespace ::com::sun::star::uno
;
34 using namespace ::oox::core
;
36 PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase
& rFragment
, PivotCacheField
& rCacheField
) :
37 WorkbookContextBase( rFragment
),
38 mrCacheField( rCacheField
)
42 ContextHandlerRef
PivotCacheFieldContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
44 switch( getCurrentElement() )
46 case XLS_TOKEN( cacheField
):
47 if( nElement
== XLS_TOKEN( sharedItems
) ) { mrCacheField
.importSharedItems( rAttribs
); return this; }
48 if( nElement
== XLS_TOKEN( fieldGroup
) ) { mrCacheField
.importFieldGroup( rAttribs
); return this; }
51 case XLS_TOKEN( fieldGroup
):
54 case XLS_TOKEN( rangePr
): mrCacheField
.importRangePr( rAttribs
); break;
55 case XLS_TOKEN( discretePr
): return this;
56 case XLS_TOKEN( groupItems
): return this;
60 case XLS_TOKEN( sharedItems
): mrCacheField
.importSharedItem( nElement
, rAttribs
); break;
61 case XLS_TOKEN( discretePr
): mrCacheField
.importDiscretePrItem( nElement
, rAttribs
); break;
62 case XLS_TOKEN( groupItems
): mrCacheField
.importGroupItem( nElement
, rAttribs
); break;
67 void PivotCacheFieldContext::onStartElement( const AttributeList
& rAttribs
)
70 mrCacheField
.importCacheField( rAttribs
);
73 ContextHandlerRef
PivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
75 switch( getCurrentElement() )
77 case BIFF12_ID_PCDFIELD
:
80 case BIFF12_ID_PCDFSHAREDITEMS
: mrCacheField
.importPCDFSharedItems( rStrm
); return this;
81 case BIFF12_ID_PCDFIELDGROUP
: mrCacheField
.importPCDFieldGroup( rStrm
); return this;
85 case BIFF12_ID_PCDFIELDGROUP
:
88 case BIFF12_ID_PCDFRANGEPR
: mrCacheField
.importPCDFRangePr( rStrm
); break;
89 case BIFF12_ID_PCDFDISCRETEPR
: return this;
90 case BIFF12_ID_PCDFGROUPITEMS
: return this;
94 case BIFF12_ID_PCDFSHAREDITEMS
: mrCacheField
.importPCDFSharedItem( nRecId
, rStrm
); break;
95 case BIFF12_ID_PCDFDISCRETEPR
: mrCacheField
.importPCDFDiscretePrItem( nRecId
, rStrm
); break;
96 case BIFF12_ID_PCDFGROUPITEMS
: mrCacheField
.importPCDFGroupItem( nRecId
, rStrm
); break;
101 void PivotCacheFieldContext::onStartRecord( SequenceInputStream
& rStrm
)
103 if( isRootElement() )
104 mrCacheField
.importPCDField( rStrm
);
107 PivotCacheDefinitionFragment::PivotCacheDefinitionFragment(
108 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
, PivotCache
& rPivotCache
) :
109 WorkbookFragmentBase( rHelper
, rFragmentPath
),
110 mrPivotCache( rPivotCache
)
114 ContextHandlerRef
PivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
116 switch( getCurrentElement() )
118 case XML_ROOT_CONTEXT
:
119 if( nElement
== XLS_TOKEN( pivotCacheDefinition
) ) { mrPivotCache
.importPivotCacheDefinition( rAttribs
); return this; }
122 case XLS_TOKEN( pivotCacheDefinition
):
125 case XLS_TOKEN( cacheSource
): mrPivotCache
.importCacheSource( rAttribs
); return this;
126 case XLS_TOKEN( cacheFields
): return this;
130 case XLS_TOKEN( cacheSource
):
131 if( nElement
== XLS_TOKEN( worksheetSource
) ) mrPivotCache
.importWorksheetSource( rAttribs
, getRelations() );
134 case XLS_TOKEN( cacheFields
):
135 if( nElement
== XLS_TOKEN( cacheField
) ) return new PivotCacheFieldContext( *this, mrPivotCache
.createCacheField() );
141 ContextHandlerRef
PivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
143 switch( getCurrentElement() )
145 case XML_ROOT_CONTEXT
:
146 if( nRecId
== BIFF12_ID_PCDEFINITION
) { mrPivotCache
.importPCDefinition( rStrm
); return this; }
149 case BIFF12_ID_PCDEFINITION
:
152 case BIFF12_ID_PCDSOURCE
: mrPivotCache
.importPCDSource( rStrm
); return this;
153 case BIFF12_ID_PCDFIELDS
: return this;
157 case BIFF12_ID_PCDSOURCE
:
158 if( nRecId
== BIFF12_ID_PCDSHEETSOURCE
) mrPivotCache
.importPCDSheetSource( rStrm
, getRelations() );
161 case BIFF12_ID_PCDFIELDS
:
162 if( nRecId
== BIFF12_ID_PCDFIELD
) return new PivotCacheFieldContext( *this, mrPivotCache
.createCacheField() );
168 const RecordInfo
* PivotCacheDefinitionFragment::getRecordInfos() const
170 static const RecordInfo spRecInfos
[] =
172 { BIFF12_ID_PCDEFINITION
, BIFF12_ID_PCDEFINITION
+ 1 },
173 { BIFF12_ID_PCDFDISCRETEPR
, BIFF12_ID_PCDFDISCRETEPR
+ 1 },
174 { BIFF12_ID_PCDFGROUPITEMS
, BIFF12_ID_PCDFGROUPITEMS
+ 1 },
175 { BIFF12_ID_PCDFIELD
, BIFF12_ID_PCDFIELD
+ 1 },
176 { BIFF12_ID_PCDFIELDGROUP
, BIFF12_ID_PCDFIELDGROUP
+ 1 },
177 { BIFF12_ID_PCDFIELDS
, BIFF12_ID_PCDFIELDS
+ 1 },
178 { BIFF12_ID_PCDFRANGEPR
, BIFF12_ID_PCDFRANGEPR
+ 1 },
179 { BIFF12_ID_PCDFSHAREDITEMS
, BIFF12_ID_PCDFSHAREDITEMS
+ 1 },
180 { BIFF12_ID_PCITEM_ARRAY
, BIFF12_ID_PCITEM_ARRAY
+ 1 },
181 { BIFF12_ID_PCDSHEETSOURCE
, BIFF12_ID_PCDSHEETSOURCE
+ 1 },
182 { BIFF12_ID_PCDSOURCE
, BIFF12_ID_PCDSOURCE
+ 1 },
188 void PivotCacheDefinitionFragment::finalizeImport()
190 // finalize the cache (check source range etc.)
191 mrPivotCache
.finalizeImport();
193 // load the cache records, if the cache is based on a deleted or an external worksheet
194 if( mrPivotCache
.isValidDataSource() && mrPivotCache
.isBasedOnDummySheet() )
196 OUString aRecFragmentPath
= getRelations().getFragmentPathFromRelId( mrPivotCache
.getRecordsRelId() );
197 if( !aRecFragmentPath
.isEmpty() )
199 sal_Int16 nSheet
= mrPivotCache
.getSourceRange().Sheet
;
200 WorksheetGlobalsRef xSheetGlob
= WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET
, nSheet
);
201 if( xSheetGlob
.get() )
202 importOoxFragment( new PivotCacheRecordsFragment( *xSheetGlob
, aRecFragmentPath
, mrPivotCache
) );
207 PivotCacheRecordsFragment::PivotCacheRecordsFragment( const WorksheetHelper
& rHelper
,
208 const OUString
& rFragmentPath
, const PivotCache
& rPivotCache
) :
209 WorksheetFragmentBase( rHelper
, rFragmentPath
),
210 mrPivotCache( rPivotCache
),
215 sal_Int32 nSheetCount
= rPivotCache
.getWorksheets().getAllSheetCount();
217 // prepare sheet: insert column header names into top row
218 rPivotCache
.writeSourceHeaderCells( *this );
219 // resize formula buffers since we've added a new dummy sheet
220 rHelper
.getFormulaBuffer().SetSheetCount( nSheetCount
);
223 ContextHandlerRef
PivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
225 switch( getCurrentElement() )
227 case XML_ROOT_CONTEXT
:
228 if( nElement
== XLS_TOKEN( pivotCacheRecords
) ) return this;
231 case XLS_TOKEN( pivotCacheRecords
):
232 if( nElement
== XLS_TOKEN( r
) ) { startCacheRecord(); return this; }
237 PivotCacheItem aItem
;
240 case XLS_TOKEN( m
): break;
241 case XLS_TOKEN( s
): aItem
.readString( rAttribs
); break;
242 case XLS_TOKEN( n
): aItem
.readNumeric( rAttribs
); break;
243 case XLS_TOKEN( d
): aItem
.readDate( rAttribs
); break;
244 case XLS_TOKEN( b
): aItem
.readBool( rAttribs
); break;
245 case XLS_TOKEN( e
): aItem
.readError( rAttribs
, getUnitConverter() ); break;
246 case XLS_TOKEN( x
): aItem
.readIndex( rAttribs
); break;
247 default: OSL_FAIL( "OoxPivotCacheRecordsFragment::onCreateContext - unexpected element" );
249 mrPivotCache
.writeSourceDataCell( *this, mnColIdx
, mnRowIdx
, aItem
);
257 ContextHandlerRef
PivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
259 switch( getCurrentElement() )
261 case XML_ROOT_CONTEXT
:
262 if( nRecId
== BIFF12_ID_PCRECORDS
) return this;
265 case BIFF12_ID_PCRECORDS
:
268 case BIFF12_ID_PCRECORD
: importPCRecord( rStrm
); break;
269 case BIFF12_ID_PCRECORDDT
: startCacheRecord(); break;
270 default: importPCRecordItem( nRecId
, rStrm
); break;
277 const RecordInfo
* PivotCacheRecordsFragment::getRecordInfos() const
279 static const RecordInfo spRecInfos
[] =
281 { BIFF12_ID_PCRECORDS
, BIFF12_ID_PCRECORDS
+ 1 },
287 // private --------------------------------------------------------------------
289 void PivotCacheRecordsFragment::startCacheRecord()
296 void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream
& rStrm
)
299 mrPivotCache
.importPCRecord( rStrm
, *this, mnRowIdx
);
303 void PivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
307 PivotCacheItem aItem
;
310 case BIFF12_ID_PCITEM_MISSING
: break;
311 case BIFF12_ID_PCITEM_STRING
: aItem
.readString( rStrm
); break;
312 case BIFF12_ID_PCITEM_DOUBLE
: aItem
.readDouble( rStrm
); break;
313 case BIFF12_ID_PCITEM_DATE
: aItem
.readDate( rStrm
); break;
314 case BIFF12_ID_PCITEM_BOOL
: aItem
.readBool( rStrm
); break;
315 case BIFF12_ID_PCITEM_ERROR
: aItem
.readError( rStrm
); break;
316 case BIFF12_ID_PCITEM_INDEX
: aItem
.readIndex( rStrm
); break;
317 default: OSL_FAIL( "OoxPivotCacheRecordsFragment::importPCRecordItem - unexpected record" );
319 mrPivotCache
.writeSourceDataCell( *this, mnColIdx
, mnRowIdx
, aItem
);
326 bool lclSeekToPCDField( BiffInputStream
& rStrm
)
328 sal_Int64 nRecHandle
= rStrm
.getRecHandle();
329 while( rStrm
.startNextRecord() )
330 if( rStrm
.getRecId() == BIFF_ID_PCDFIELD
)
332 rStrm
.startRecordByHandle( nRecHandle
);
338 BiffPivotCacheFragment::BiffPivotCacheFragment(
339 const WorkbookHelper
& rHelper
, const OUString
& rStrmName
, PivotCache
& rPivotCache
) :
340 BiffWorkbookFragmentBase( rHelper
, rStrmName
, true ),
341 mrPivotCache( rPivotCache
)
345 bool BiffPivotCacheFragment::importFragment()
347 BiffInputStream
& rStrm
= getInputStream();
348 if( rStrm
.startNextRecord() && (rStrm
.getRecId() == BIFF_ID_PCDEFINITION
) )
350 // read PCDEFINITION and optional PCDEFINITION2 records
351 mrPivotCache
.importPCDefinition( rStrm
);
353 // read cache fields as long as another PCDFIELD record can be found
354 while( lclSeekToPCDField( rStrm
) )
355 mrPivotCache
.createCacheField( true ).importPCDField( rStrm
);
357 // finalize the cache (check source range etc.)
358 mrPivotCache
.finalizeImport();
360 // load the cache records, if the cache is based on a deleted or an external worksheet
361 if( mrPivotCache
.isValidDataSource() && mrPivotCache
.isBasedOnDummySheet() )
363 /* Last call of lclSeekToPCDField() failed and kept stream position
364 unchanged. Stream should point to source data table now. */
365 sal_Int16 nSheet
= mrPivotCache
.getSourceRange().Sheet
;
366 WorksheetGlobalsRef xSheetGlob
= WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET
, nSheet
);
367 if( xSheetGlob
.get() )
369 BiffPivotCacheRecordsContext
aContext( *xSheetGlob
, mrPivotCache
);
370 while( rStrm
.startNextRecord() && (rStrm
.getRecId() != BIFF_ID_EOF
) )
371 aContext
.importRecord( rStrm
);
376 return rStrm
.getRecId() == BIFF_ID_EOF
;
379 BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext( const WorksheetHelper
& rHelper
, const PivotCache
& rPivotCache
) :
380 BiffWorksheetContextBase( rHelper
),
381 mrPivotCache( rPivotCache
),
384 mbHasShared( false ),
387 // prepare sheet: insert column header names into top row
388 mrPivotCache
.writeSourceHeaderCells( *this );
390 // find all fields without shared items, remember column indexes in source data
391 for( sal_Int32 nFieldIdx
= 0, nFieldCount
= mrPivotCache
.getCacheFieldCount(), nCol
= 0; nFieldIdx
< nFieldCount
; ++nFieldIdx
)
393 const PivotCacheField
* pCacheField
= mrPivotCache
.getCacheField( nFieldIdx
);
394 if( pCacheField
&& pCacheField
->isDatabaseField() )
396 if( pCacheField
->hasSharedItems() )
399 maUnsharedCols
.push_back( nCol
);
405 void BiffPivotCacheRecordsContext::importRecord( BiffInputStream
& rStrm
)
407 if( rStrm
.getRecId() == BIFF_ID_PCITEM_INDEXLIST
)
409 OSL_ENSURE( mbHasShared
, "BiffPivotCacheRecordsContext::importRecord - unexpected PCITEM_INDEXLIST record" );
410 // PCITEM_INDEXLIST record always in front of a new data row
412 mrPivotCache
.importPCItemIndexList( rStrm
, *this, mnRowIdx
);
413 mbInRow
= !maUnsharedCols
.empty(); // mbInRow remains true, if unshared items are expected
417 PivotCacheItem aItem
;
418 switch( rStrm
.getRecId() )
420 case BIFF_ID_PCITEM_MISSING
: break;
421 case BIFF_ID_PCITEM_STRING
: aItem
.readString( rStrm
, *this ); break;
422 case BIFF_ID_PCITEM_DOUBLE
: aItem
.readDouble( rStrm
); break;
423 case BIFF_ID_PCITEM_INTEGER
: aItem
.readInteger( rStrm
); break;
424 case BIFF_ID_PCITEM_DATE
: aItem
.readDate( rStrm
); break;
425 case BIFF_ID_PCITEM_BOOL
: aItem
.readBool( rStrm
); break;
426 case BIFF_ID_PCITEM_ERROR
: aItem
.readError( rStrm
); break;
427 default: return; // unknown record, ignore
430 // find next column index, might start new row if no fields with shared items exist
431 if( mbInRow
&& (mnColIdx
== maUnsharedCols
.size()) )
433 OSL_ENSURE( !mbHasShared
, "BiffPivotCacheRecordsContext::importRecord - PCITEM_INDEXLIST record missing" );
434 mbInRow
= mbHasShared
; // do not leave current row if PCITEM_INDEXLIST is expected
436 // start next row on first call, or on row wrap without shared items
440 // write the item data to the sheet cell
441 OSL_ENSURE( mnColIdx
< maUnsharedCols
.size(), "BiffPivotCacheRecordsContext::importRecord - invalid column index" );
442 if( mnColIdx
< maUnsharedCols
.size() )
443 mrPivotCache
.writeSourceDataCell( *this, maUnsharedCols
[ mnColIdx
], mnRowIdx
, aItem
);
447 void BiffPivotCacheRecordsContext::startNextRow()
457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */