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