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 43 PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase& rFragment, PivotCacheField& rCacheField ) : 44 WorkbookContextBase( rFragment ), 45 mrCacheField( rCacheField ) 46 { 47 } 48 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 74 void PivotCacheFieldContext::onStartElement( const AttributeList& rAttribs ) 75 { 76 if( isRootElement() ) 77 mrCacheField.importCacheField( rAttribs ); 78 } 79 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 108 void PivotCacheFieldContext::onStartRecord( SequenceInputStream& rStrm ) 109 { 110 if( isRootElement() ) 111 mrCacheField.importPCDField( rStrm ); 112 } 113 114 // ============================================================================ 115 116 PivotCacheDefinitionFragment::PivotCacheDefinitionFragment( 117 const WorkbookHelper& rHelper, const OUString& rFragmentPath, PivotCache& rPivotCache ) : 118 WorkbookFragmentBase( rHelper, rFragmentPath ), 119 mrPivotCache( rPivotCache ) 120 { 121 } 122 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 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 177 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 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 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 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 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 284 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 296 void PivotCacheRecordsFragment::startCacheRecord() 297 { 298 mnColIdx = 0; 299 ++mnRowIdx; 300 mbInRecord = true; 301 } 302 303 void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream& rStrm ) 304 { 305 startCacheRecord(); 306 mrPivotCache.importPCRecord( rStrm, *this, mnRowIdx ); 307 mbInRecord = false; 308 } 309 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 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 350 BiffPivotCacheFragment::BiffPivotCacheFragment( 351 const WorkbookHelper& rHelper, const OUString& rStrmName, PivotCache& rPivotCache ) : 352 BiffWorkbookFragmentBase( rHelper, rStrmName, true ), 353 mrPivotCache( rPivotCache ) 354 { 355 } 356 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 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 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 461 void BiffPivotCacheRecordsContext::startNextRow() 462 { 463 mnColIdx = 0; 464 ++mnRowIdx; 465 mbInRow = true; 466 } 467 468 // ============================================================================ 469 470 } // namespace xls 471 } // namespace oox 472