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