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