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