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 #include "oox/xls/pivotcachefragment.hxx"
25
26 #include "oox/helper/attributelist.hxx"
27 #include "oox/xls/addressconverter.hxx"
28 #include "oox/xls/biffinputstream.hxx"
29 #include "oox/xls/pivotcachebuffer.hxx"
30
31 namespace oox {
32 namespace xls {
33
34 // ============================================================================
35
36 using namespace ::com::sun::star::uno;
37 using namespace ::oox::core;
38
39 using ::rtl::OUString;
40
41 // ============================================================================
42
PivotCacheFieldContext(WorkbookFragmentBase & rFragment,PivotCacheField & rCacheField)43 PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase& rFragment, PivotCacheField& rCacheField ) :
44 WorkbookContextBase( rFragment ),
45 mrCacheField( rCacheField )
46 {
47 }
48
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)49 ContextHandlerRef PivotCacheFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
50 {
51 switch( getCurrentElement() )
52 {
53 case XLS_TOKEN( cacheField ):
54 if( nElement == XLS_TOKEN( sharedItems ) ) { mrCacheField.importSharedItems( rAttribs ); return this; }
55 if( nElement == XLS_TOKEN( fieldGroup ) ) { mrCacheField.importFieldGroup( rAttribs ); return this; }
56 break;
57
58 case XLS_TOKEN( fieldGroup ):
59 switch( nElement )
60 {
61 case XLS_TOKEN( rangePr ): mrCacheField.importRangePr( rAttribs ); break;
62 case XLS_TOKEN( discretePr ): return this;
63 case XLS_TOKEN( groupItems ): return this;
64 }
65 break;
66
67 case XLS_TOKEN( sharedItems ): mrCacheField.importSharedItem( nElement, rAttribs ); break;
68 case XLS_TOKEN( discretePr ): mrCacheField.importDiscretePrItem( nElement, rAttribs ); break;
69 case XLS_TOKEN( groupItems ): mrCacheField.importGroupItem( nElement, rAttribs ); break;
70 }
71 return 0;
72 }
73
onStartElement(const AttributeList & rAttribs)74 void PivotCacheFieldContext::onStartElement( const AttributeList& rAttribs )
75 {
76 if( isRootElement() )
77 mrCacheField.importCacheField( rAttribs );
78 }
79
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)80 ContextHandlerRef PivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
81 {
82 switch( getCurrentElement() )
83 {
84 case BIFF12_ID_PCDFIELD:
85 switch( nRecId )
86 {
87 case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItems( rStrm ); return this;
88 case BIFF12_ID_PCDFIELDGROUP: mrCacheField.importPCDFieldGroup( rStrm ); return this;
89 }
90 break;
91
92 case BIFF12_ID_PCDFIELDGROUP:
93 switch( nRecId )
94 {
95 case BIFF12_ID_PCDFRANGEPR: mrCacheField.importPCDFRangePr( rStrm ); break;
96 case BIFF12_ID_PCDFDISCRETEPR: return this;
97 case BIFF12_ID_PCDFGROUPITEMS: return this;
98 }
99 break;
100
101 case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItem( nRecId, rStrm ); break;
102 case BIFF12_ID_PCDFDISCRETEPR: mrCacheField.importPCDFDiscretePrItem( nRecId, rStrm ); break;
103 case BIFF12_ID_PCDFGROUPITEMS: mrCacheField.importPCDFGroupItem( nRecId, rStrm ); break;
104 }
105 return 0;
106 }
107
onStartRecord(SequenceInputStream & rStrm)108 void PivotCacheFieldContext::onStartRecord( SequenceInputStream& rStrm )
109 {
110 if( isRootElement() )
111 mrCacheField.importPCDField( rStrm );
112 }
113
114 // ============================================================================
115
PivotCacheDefinitionFragment(const WorkbookHelper & rHelper,const OUString & rFragmentPath,PivotCache & rPivotCache)116 PivotCacheDefinitionFragment::PivotCacheDefinitionFragment(
117 const WorkbookHelper& rHelper, const OUString& rFragmentPath, PivotCache& rPivotCache ) :
118 WorkbookFragmentBase( rHelper, rFragmentPath ),
119 mrPivotCache( rPivotCache )
120 {
121 }
122
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)123 ContextHandlerRef PivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
124 {
125 switch( getCurrentElement() )
126 {
127 case XML_ROOT_CONTEXT:
128 if( nElement == XLS_TOKEN( pivotCacheDefinition ) ) { mrPivotCache.importPivotCacheDefinition( rAttribs ); return this; }
129 break;
130
131 case XLS_TOKEN( pivotCacheDefinition ):
132 switch( nElement )
133 {
134 case XLS_TOKEN( cacheSource ): mrPivotCache.importCacheSource( rAttribs ); return this;
135 case XLS_TOKEN( cacheFields ): return this;
136 }
137 break;
138
139 case XLS_TOKEN( cacheSource ):
140 if( nElement == XLS_TOKEN( worksheetSource ) ) mrPivotCache.importWorksheetSource( rAttribs, getRelations() );
141 break;
142
143 case XLS_TOKEN( cacheFields ):
144 if( nElement == XLS_TOKEN( cacheField ) ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
145 break;
146 }
147 return 0;
148 }
149
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)150 ContextHandlerRef PivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
151 {
152 switch( getCurrentElement() )
153 {
154 case XML_ROOT_CONTEXT:
155 if( nRecId == BIFF12_ID_PCDEFINITION ) { mrPivotCache.importPCDefinition( rStrm ); return this; }
156 break;
157
158 case BIFF12_ID_PCDEFINITION:
159 switch( nRecId )
160 {
161 case BIFF12_ID_PCDSOURCE: mrPivotCache.importPCDSource( rStrm ); return this;
162 case BIFF12_ID_PCDFIELDS: return this;
163 }
164 break;
165
166 case BIFF12_ID_PCDSOURCE:
167 if( nRecId == BIFF12_ID_PCDSHEETSOURCE ) mrPivotCache.importPCDSheetSource( rStrm, getRelations() );
168 break;
169
170 case BIFF12_ID_PCDFIELDS:
171 if( nRecId == BIFF12_ID_PCDFIELD ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
172 break;
173 }
174 return 0;
175 }
176
getRecordInfos() const177 const RecordInfo* PivotCacheDefinitionFragment::getRecordInfos() const
178 {
179 static const RecordInfo spRecInfos[] =
180 {
181 { BIFF12_ID_PCDEFINITION, BIFF12_ID_PCDEFINITION + 1 },
182 { BIFF12_ID_PCDFDISCRETEPR, BIFF12_ID_PCDFDISCRETEPR + 1 },
183 { BIFF12_ID_PCDFGROUPITEMS, BIFF12_ID_PCDFGROUPITEMS + 1 },
184 { BIFF12_ID_PCDFIELD, BIFF12_ID_PCDFIELD + 1 },
185 { BIFF12_ID_PCDFIELDGROUP, BIFF12_ID_PCDFIELDGROUP + 1 },
186 { BIFF12_ID_PCDFIELDS, BIFF12_ID_PCDFIELDS + 1 },
187 { BIFF12_ID_PCDFRANGEPR, BIFF12_ID_PCDFRANGEPR + 1 },
188 { BIFF12_ID_PCDFSHAREDITEMS, BIFF12_ID_PCDFSHAREDITEMS + 1 },
189 { BIFF12_ID_PCITEM_ARRAY, BIFF12_ID_PCITEM_ARRAY + 1 },
190 { BIFF12_ID_PCDSHEETSOURCE, BIFF12_ID_PCDSHEETSOURCE + 1 },
191 { BIFF12_ID_PCDSOURCE, BIFF12_ID_PCDSOURCE + 1 },
192 { -1, -1 }
193 };
194 return spRecInfos;
195 }
196
finalizeImport()197 void PivotCacheDefinitionFragment::finalizeImport()
198 {
199 // finalize the cache (check source range etc.)
200 mrPivotCache.finalizeImport();
201
202 // load the cache records, if the cache is based on a deleted or an external worksheet
203 if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() )
204 {
205 OUString aRecFragmentPath = getRelations().getFragmentPathFromRelId( mrPivotCache.getRecordsRelId() );
206 if( aRecFragmentPath.getLength() > 0 )
207 {
208 sal_Int16 nSheet = mrPivotCache.getSourceRange().Sheet;
209 WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, nSheet );
210 if( xSheetGlob.get() )
211 importOoxFragment( new PivotCacheRecordsFragment( *xSheetGlob, aRecFragmentPath, mrPivotCache ) );
212 }
213 }
214 }
215
216 // ============================================================================
217
PivotCacheRecordsFragment(const WorksheetHelper & rHelper,const OUString & rFragmentPath,const PivotCache & rPivotCache)218 PivotCacheRecordsFragment::PivotCacheRecordsFragment( const WorksheetHelper& rHelper,
219 const OUString& rFragmentPath, const PivotCache& rPivotCache ) :
220 WorksheetFragmentBase( rHelper, rFragmentPath ),
221 mrPivotCache( rPivotCache ),
222 mnColIdx( 0 ),
223 mnRowIdx( 0 ),
224 mbInRecord( false )
225 {
226 // prepare sheet: insert column header names into top row
227 rPivotCache.writeSourceHeaderCells( *this );
228 }
229
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)230 ContextHandlerRef PivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
231 {
232 switch( getCurrentElement() )
233 {
234 case XML_ROOT_CONTEXT:
235 if( nElement == XLS_TOKEN( pivotCacheRecords ) ) return this;
236 break;
237
238 case XLS_TOKEN( pivotCacheRecords ):
239 if( nElement == XLS_TOKEN( r ) ) { startCacheRecord(); return this; }
240 break;
241
242 case XLS_TOKEN( r ):
243 {
244 PivotCacheItem aItem;
245 switch( nElement )
246 {
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, "PivotCacheRecordsFragment::onCreateContext - unexpected element" );
255 }
256 mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
257 ++mnColIdx;
258 }
259 break;
260 }
261 return 0;
262 }
263
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)264 ContextHandlerRef PivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
265 {
266 switch( getCurrentElement() )
267 {
268 case XML_ROOT_CONTEXT:
269 if( nRecId == BIFF12_ID_PCRECORDS ) return this;
270 break;
271
272 case BIFF12_ID_PCRECORDS:
273 switch( nRecId )
274 {
275 case BIFF12_ID_PCRECORD: importPCRecord( rStrm ); break;
276 case BIFF12_ID_PCRECORDDT: startCacheRecord(); break;
277 default: importPCRecordItem( nRecId, rStrm ); break;
278 }
279 break;
280 }
281 return 0;
282 }
283
getRecordInfos() const284 const RecordInfo* PivotCacheRecordsFragment::getRecordInfos() const
285 {
286 static const RecordInfo spRecInfos[] =
287 {
288 { BIFF12_ID_PCRECORDS, BIFF12_ID_PCRECORDS + 1 },
289 { -1, -1 }
290 };
291 return spRecInfos;
292 }
293
294 // private --------------------------------------------------------------------
295
startCacheRecord()296 void PivotCacheRecordsFragment::startCacheRecord()
297 {
298 mnColIdx = 0;
299 ++mnRowIdx;
300 mbInRecord = true;
301 }
302
importPCRecord(SequenceInputStream & rStrm)303 void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream& rStrm )
304 {
305 startCacheRecord();
306 mrPivotCache.importPCRecord( rStrm, *this, mnRowIdx );
307 mbInRecord = false;
308 }
309
importPCRecordItem(sal_Int32 nRecId,SequenceInputStream & rStrm)310 void PivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
311 {
312 if( mbInRecord )
313 {
314 PivotCacheItem aItem;
315 switch( nRecId )
316 {
317 case BIFF12_ID_PCITEM_MISSING: break;
318 case BIFF12_ID_PCITEM_STRING: aItem.readString( rStrm ); break;
319 case BIFF12_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break;
320 case BIFF12_ID_PCITEM_DATE: aItem.readDate( rStrm ); break;
321 case BIFF12_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break;
322 case BIFF12_ID_PCITEM_ERROR: aItem.readError( rStrm ); break;
323 case BIFF12_ID_PCITEM_INDEX: aItem.readIndex( rStrm ); break;
324 default: OSL_ENSURE( false, "PivotCacheRecordsFragment::importPCRecordItem - unexpected record" );
325 }
326 mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
327 ++mnColIdx;
328 }
329 }
330
331 // ============================================================================
332 // ============================================================================
333
334 namespace {
335
lclSeekToPCDField(BiffInputStream & rStrm)336 bool lclSeekToPCDField( BiffInputStream& rStrm )
337 {
338 sal_Int64 nRecHandle = rStrm.getRecHandle();
339 while( rStrm.startNextRecord() )
340 if( rStrm.getRecId() == BIFF_ID_PCDFIELD )
341 return true;
342 rStrm.startRecordByHandle( nRecHandle );
343 return false;
344 }
345
346 } // namespace
347
348 // ----------------------------------------------------------------------------
349
BiffPivotCacheFragment(const WorkbookHelper & rHelper,const OUString & rStrmName,PivotCache & rPivotCache)350 BiffPivotCacheFragment::BiffPivotCacheFragment(
351 const WorkbookHelper& rHelper, const OUString& rStrmName, PivotCache& rPivotCache ) :
352 BiffWorkbookFragmentBase( rHelper, rStrmName, true ),
353 mrPivotCache( rPivotCache )
354 {
355 }
356
importFragment()357 bool BiffPivotCacheFragment::importFragment()
358 {
359 BiffInputStream& rStrm = getInputStream();
360 if( rStrm.startNextRecord() && (rStrm.getRecId() == BIFF_ID_PCDEFINITION) )
361 {
362 // read PCDEFINITION and optional PCDEFINITION2 records
363 mrPivotCache.importPCDefinition( rStrm );
364
365 // read cache fields as long as another PCDFIELD record can be found
366 while( lclSeekToPCDField( rStrm ) )
367 mrPivotCache.createCacheField( true ).importPCDField( rStrm );
368
369 // finalize the cache (check source range etc.)
370 mrPivotCache.finalizeImport();
371
372 // load the cache records, if the cache is based on a deleted or an external worksheet
373 if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() )
374 {
375 /* Last call of lclSeekToPCDField() failed and kept stream position
376 unchanged. Stream should point to source data table now. */
377 sal_Int16 nSheet = mrPivotCache.getSourceRange().Sheet;
378 WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, nSheet );
379 if( xSheetGlob.get() )
380 {
381 BiffPivotCacheRecordsContext aContext( *xSheetGlob, mrPivotCache );
382 while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) )
383 aContext.importRecord( rStrm );
384 }
385 }
386 }
387
388 return rStrm.getRecId() == BIFF_ID_EOF;
389 }
390
391 // ============================================================================
392
BiffPivotCacheRecordsContext(const WorksheetHelper & rHelper,const PivotCache & rPivotCache)393 BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext( const WorksheetHelper& rHelper, const PivotCache& rPivotCache ) :
394 BiffWorksheetContextBase( rHelper ),
395 mrPivotCache( rPivotCache ),
396 mnColIdx( 0 ),
397 mnRowIdx( 0 ),
398 mbHasShared( false ),
399 mbInRow( false )
400 {
401 // prepare sheet: insert column header names into top row
402 mrPivotCache.writeSourceHeaderCells( *this );
403
404 // find all fields without shared items, remember column indexes in source data
405 for( sal_Int32 nFieldIdx = 0, nFieldCount = mrPivotCache.getCacheFieldCount(), nCol = 0; nFieldIdx < nFieldCount; ++nFieldIdx )
406 {
407 const PivotCacheField* pCacheField = mrPivotCache.getCacheField( nFieldIdx );
408 if( pCacheField && pCacheField->isDatabaseField() )
409 {
410 if( pCacheField->hasSharedItems() )
411 mbHasShared = true;
412 else
413 maUnsharedCols.push_back( nCol );
414 ++nCol;
415 }
416 }
417 }
418
importRecord(BiffInputStream & rStrm)419 void BiffPivotCacheRecordsContext::importRecord( BiffInputStream& rStrm )
420 {
421 if( rStrm.getRecId() == BIFF_ID_PCITEM_INDEXLIST )
422 {
423 OSL_ENSURE( mbHasShared, "BiffPivotCacheRecordsContext::importRecord - unexpected PCITEM_INDEXLIST record" );
424 // PCITEM_INDEXLIST record always in front of a new data row
425 startNextRow();
426 mrPivotCache.importPCItemIndexList( rStrm, *this, mnRowIdx );
427 mbInRow = !maUnsharedCols.empty(); // mbInRow remains true, if unshared items are expected
428 return;
429 }
430
431 PivotCacheItem aItem;
432 switch( rStrm.getRecId() )
433 {
434 case BIFF_ID_PCITEM_MISSING: break;
435 case BIFF_ID_PCITEM_STRING: aItem.readString( rStrm, *this ); break;
436 case BIFF_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break;
437 case BIFF_ID_PCITEM_INTEGER: aItem.readInteger( rStrm ); break;
438 case BIFF_ID_PCITEM_DATE: aItem.readDate( rStrm ); break;
439 case BIFF_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break;
440 case BIFF_ID_PCITEM_ERROR: aItem.readError( rStrm ); break;
441 default: return; // unknown record, ignore
442 }
443
444 // find next column index, might start new row if no fields with shared items exist
445 if( mbInRow && (mnColIdx == maUnsharedCols.size()) )
446 {
447 OSL_ENSURE( !mbHasShared, "BiffPivotCacheRecordsContext::importRecord - PCITEM_INDEXLIST record missing" );
448 mbInRow = mbHasShared; // do not leave current row if PCITEM_INDEXLIST is expected
449 }
450 // start next row on first call, or on row wrap without shared items
451 if( !mbInRow )
452 startNextRow();
453
454 // write the item data to the sheet cell
455 OSL_ENSURE( mnColIdx < maUnsharedCols.size(), "BiffPivotCacheRecordsContext::importRecord - invalid column index" );
456 if( mnColIdx < maUnsharedCols.size() )
457 mrPivotCache.writeSourceDataCell( *this, maUnsharedCols[ mnColIdx ], mnRowIdx, aItem );
458 ++mnColIdx;
459 }
460
startNextRow()461 void BiffPivotCacheRecordsContext::startNextRow()
462 {
463 mnColIdx = 0;
464 ++mnRowIdx;
465 mbInRow = true;
466 }
467
468 // ============================================================================
469
470 } // namespace xls
471 } // namespace oox
472