| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "oox/xls/pivotcachefragment.hxx" |
| |
| #include "oox/helper/attributelist.hxx" |
| #include "oox/xls/addressconverter.hxx" |
| #include "oox/xls/biffinputstream.hxx" |
| #include "oox/xls/pivotcachebuffer.hxx" |
| |
| namespace oox { |
| namespace xls { |
| |
| // ============================================================================ |
| |
| using namespace ::com::sun::star::uno; |
| using namespace ::oox::core; |
| |
| using ::rtl::OUString; |
| |
| // ============================================================================ |
| |
| PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase& rFragment, PivotCacheField& rCacheField ) : |
| WorkbookContextBase( rFragment ), |
| mrCacheField( rCacheField ) |
| { |
| } |
| |
| ContextHandlerRef PivotCacheFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case XLS_TOKEN( cacheField ): |
| if( nElement == XLS_TOKEN( sharedItems ) ) { mrCacheField.importSharedItems( rAttribs ); return this; } |
| if( nElement == XLS_TOKEN( fieldGroup ) ) { mrCacheField.importFieldGroup( rAttribs ); return this; } |
| break; |
| |
| case XLS_TOKEN( fieldGroup ): |
| switch( nElement ) |
| { |
| case XLS_TOKEN( rangePr ): mrCacheField.importRangePr( rAttribs ); break; |
| case XLS_TOKEN( discretePr ): return this; |
| case XLS_TOKEN( groupItems ): return this; |
| } |
| break; |
| |
| case XLS_TOKEN( sharedItems ): mrCacheField.importSharedItem( nElement, rAttribs ); break; |
| case XLS_TOKEN( discretePr ): mrCacheField.importDiscretePrItem( nElement, rAttribs ); break; |
| case XLS_TOKEN( groupItems ): mrCacheField.importGroupItem( nElement, rAttribs ); break; |
| } |
| return 0; |
| } |
| |
| void PivotCacheFieldContext::onStartElement( const AttributeList& rAttribs ) |
| { |
| if( isRootElement() ) |
| mrCacheField.importCacheField( rAttribs ); |
| } |
| |
| ContextHandlerRef PivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case BIFF12_ID_PCDFIELD: |
| switch( nRecId ) |
| { |
| case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItems( rStrm ); return this; |
| case BIFF12_ID_PCDFIELDGROUP: mrCacheField.importPCDFieldGroup( rStrm ); return this; |
| } |
| break; |
| |
| case BIFF12_ID_PCDFIELDGROUP: |
| switch( nRecId ) |
| { |
| case BIFF12_ID_PCDFRANGEPR: mrCacheField.importPCDFRangePr( rStrm ); break; |
| case BIFF12_ID_PCDFDISCRETEPR: return this; |
| case BIFF12_ID_PCDFGROUPITEMS: return this; |
| } |
| break; |
| |
| case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItem( nRecId, rStrm ); break; |
| case BIFF12_ID_PCDFDISCRETEPR: mrCacheField.importPCDFDiscretePrItem( nRecId, rStrm ); break; |
| case BIFF12_ID_PCDFGROUPITEMS: mrCacheField.importPCDFGroupItem( nRecId, rStrm ); break; |
| } |
| return 0; |
| } |
| |
| void PivotCacheFieldContext::onStartRecord( SequenceInputStream& rStrm ) |
| { |
| if( isRootElement() ) |
| mrCacheField.importPCDField( rStrm ); |
| } |
| |
| // ============================================================================ |
| |
| PivotCacheDefinitionFragment::PivotCacheDefinitionFragment( |
| const WorkbookHelper& rHelper, const OUString& rFragmentPath, PivotCache& rPivotCache ) : |
| WorkbookFragmentBase( rHelper, rFragmentPath ), |
| mrPivotCache( rPivotCache ) |
| { |
| } |
| |
| ContextHandlerRef PivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case XML_ROOT_CONTEXT: |
| if( nElement == XLS_TOKEN( pivotCacheDefinition ) ) { mrPivotCache.importPivotCacheDefinition( rAttribs ); return this; } |
| break; |
| |
| case XLS_TOKEN( pivotCacheDefinition ): |
| switch( nElement ) |
| { |
| case XLS_TOKEN( cacheSource ): mrPivotCache.importCacheSource( rAttribs ); return this; |
| case XLS_TOKEN( cacheFields ): return this; |
| } |
| break; |
| |
| case XLS_TOKEN( cacheSource ): |
| if( nElement == XLS_TOKEN( worksheetSource ) ) mrPivotCache.importWorksheetSource( rAttribs, getRelations() ); |
| break; |
| |
| case XLS_TOKEN( cacheFields ): |
| if( nElement == XLS_TOKEN( cacheField ) ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() ); |
| break; |
| } |
| return 0; |
| } |
| |
| ContextHandlerRef PivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case XML_ROOT_CONTEXT: |
| if( nRecId == BIFF12_ID_PCDEFINITION ) { mrPivotCache.importPCDefinition( rStrm ); return this; } |
| break; |
| |
| case BIFF12_ID_PCDEFINITION: |
| switch( nRecId ) |
| { |
| case BIFF12_ID_PCDSOURCE: mrPivotCache.importPCDSource( rStrm ); return this; |
| case BIFF12_ID_PCDFIELDS: return this; |
| } |
| break; |
| |
| case BIFF12_ID_PCDSOURCE: |
| if( nRecId == BIFF12_ID_PCDSHEETSOURCE ) mrPivotCache.importPCDSheetSource( rStrm, getRelations() ); |
| break; |
| |
| case BIFF12_ID_PCDFIELDS: |
| if( nRecId == BIFF12_ID_PCDFIELD ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() ); |
| break; |
| } |
| return 0; |
| } |
| |
| const RecordInfo* PivotCacheDefinitionFragment::getRecordInfos() const |
| { |
| static const RecordInfo spRecInfos[] = |
| { |
| { BIFF12_ID_PCDEFINITION, BIFF12_ID_PCDEFINITION + 1 }, |
| { BIFF12_ID_PCDFDISCRETEPR, BIFF12_ID_PCDFDISCRETEPR + 1 }, |
| { BIFF12_ID_PCDFGROUPITEMS, BIFF12_ID_PCDFGROUPITEMS + 1 }, |
| { BIFF12_ID_PCDFIELD, BIFF12_ID_PCDFIELD + 1 }, |
| { BIFF12_ID_PCDFIELDGROUP, BIFF12_ID_PCDFIELDGROUP + 1 }, |
| { BIFF12_ID_PCDFIELDS, BIFF12_ID_PCDFIELDS + 1 }, |
| { BIFF12_ID_PCDFRANGEPR, BIFF12_ID_PCDFRANGEPR + 1 }, |
| { BIFF12_ID_PCDFSHAREDITEMS, BIFF12_ID_PCDFSHAREDITEMS + 1 }, |
| { BIFF12_ID_PCITEM_ARRAY, BIFF12_ID_PCITEM_ARRAY + 1 }, |
| { BIFF12_ID_PCDSHEETSOURCE, BIFF12_ID_PCDSHEETSOURCE + 1 }, |
| { BIFF12_ID_PCDSOURCE, BIFF12_ID_PCDSOURCE + 1 }, |
| { -1, -1 } |
| }; |
| return spRecInfos; |
| } |
| |
| void PivotCacheDefinitionFragment::finalizeImport() |
| { |
| // finalize the cache (check source range etc.) |
| mrPivotCache.finalizeImport(); |
| |
| // load the cache records, if the cache is based on a deleted or an external worksheet |
| if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() ) |
| { |
| OUString aRecFragmentPath = getRelations().getFragmentPathFromRelId( mrPivotCache.getRecordsRelId() ); |
| if( aRecFragmentPath.getLength() > 0 ) |
| { |
| sal_Int16 nSheet = mrPivotCache.getSourceRange().Sheet; |
| WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, nSheet ); |
| if( xSheetGlob.get() ) |
| importOoxFragment( new PivotCacheRecordsFragment( *xSheetGlob, aRecFragmentPath, mrPivotCache ) ); |
| } |
| } |
| } |
| |
| // ============================================================================ |
| |
| PivotCacheRecordsFragment::PivotCacheRecordsFragment( const WorksheetHelper& rHelper, |
| const OUString& rFragmentPath, const PivotCache& rPivotCache ) : |
| WorksheetFragmentBase( rHelper, rFragmentPath ), |
| mrPivotCache( rPivotCache ), |
| mnColIdx( 0 ), |
| mnRowIdx( 0 ), |
| mbInRecord( false ) |
| { |
| // prepare sheet: insert column header names into top row |
| rPivotCache.writeSourceHeaderCells( *this ); |
| } |
| |
| ContextHandlerRef PivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case XML_ROOT_CONTEXT: |
| if( nElement == XLS_TOKEN( pivotCacheRecords ) ) return this; |
| break; |
| |
| case XLS_TOKEN( pivotCacheRecords ): |
| if( nElement == XLS_TOKEN( r ) ) { startCacheRecord(); return this; } |
| break; |
| |
| case XLS_TOKEN( r ): |
| { |
| PivotCacheItem aItem; |
| switch( nElement ) |
| { |
| case XLS_TOKEN( m ): break; |
| case XLS_TOKEN( s ): aItem.readString( rAttribs ); break; |
| case XLS_TOKEN( n ): aItem.readNumeric( rAttribs ); break; |
| case XLS_TOKEN( d ): aItem.readDate( rAttribs ); break; |
| case XLS_TOKEN( b ): aItem.readBool( rAttribs ); break; |
| case XLS_TOKEN( e ): aItem.readError( rAttribs, getUnitConverter() ); break; |
| case XLS_TOKEN( x ): aItem.readIndex( rAttribs ); break; |
| default: OSL_ENSURE( false, "PivotCacheRecordsFragment::onCreateContext - unexpected element" ); |
| } |
| mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem ); |
| ++mnColIdx; |
| } |
| break; |
| } |
| return 0; |
| } |
| |
| ContextHandlerRef PivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) |
| { |
| switch( getCurrentElement() ) |
| { |
| case XML_ROOT_CONTEXT: |
| if( nRecId == BIFF12_ID_PCRECORDS ) return this; |
| break; |
| |
| case BIFF12_ID_PCRECORDS: |
| switch( nRecId ) |
| { |
| case BIFF12_ID_PCRECORD: importPCRecord( rStrm ); break; |
| case BIFF12_ID_PCRECORDDT: startCacheRecord(); break; |
| default: importPCRecordItem( nRecId, rStrm ); break; |
| } |
| break; |
| } |
| return 0; |
| } |
| |
| const RecordInfo* PivotCacheRecordsFragment::getRecordInfos() const |
| { |
| static const RecordInfo spRecInfos[] = |
| { |
| { BIFF12_ID_PCRECORDS, BIFF12_ID_PCRECORDS + 1 }, |
| { -1, -1 } |
| }; |
| return spRecInfos; |
| } |
| |
| // private -------------------------------------------------------------------- |
| |
| void PivotCacheRecordsFragment::startCacheRecord() |
| { |
| mnColIdx = 0; |
| ++mnRowIdx; |
| mbInRecord = true; |
| } |
| |
| void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream& rStrm ) |
| { |
| startCacheRecord(); |
| mrPivotCache.importPCRecord( rStrm, *this, mnRowIdx ); |
| mbInRecord = false; |
| } |
| |
| void PivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm ) |
| { |
| if( mbInRecord ) |
| { |
| PivotCacheItem aItem; |
| switch( nRecId ) |
| { |
| case BIFF12_ID_PCITEM_MISSING: break; |
| case BIFF12_ID_PCITEM_STRING: aItem.readString( rStrm ); break; |
| case BIFF12_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break; |
| case BIFF12_ID_PCITEM_DATE: aItem.readDate( rStrm ); break; |
| case BIFF12_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break; |
| case BIFF12_ID_PCITEM_ERROR: aItem.readError( rStrm ); break; |
| case BIFF12_ID_PCITEM_INDEX: aItem.readIndex( rStrm ); break; |
| default: OSL_ENSURE( false, "PivotCacheRecordsFragment::importPCRecordItem - unexpected record" ); |
| } |
| mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem ); |
| ++mnColIdx; |
| } |
| } |
| |
| // ============================================================================ |
| // ============================================================================ |
| |
| namespace { |
| |
| bool lclSeekToPCDField( BiffInputStream& rStrm ) |
| { |
| sal_Int64 nRecHandle = rStrm.getRecHandle(); |
| while( rStrm.startNextRecord() ) |
| if( rStrm.getRecId() == BIFF_ID_PCDFIELD ) |
| return true; |
| rStrm.startRecordByHandle( nRecHandle ); |
| return false; |
| } |
| |
| } // namespace |
| |
| // ---------------------------------------------------------------------------- |
| |
| BiffPivotCacheFragment::BiffPivotCacheFragment( |
| const WorkbookHelper& rHelper, const OUString& rStrmName, PivotCache& rPivotCache ) : |
| BiffWorkbookFragmentBase( rHelper, rStrmName, true ), |
| mrPivotCache( rPivotCache ) |
| { |
| } |
| |
| bool BiffPivotCacheFragment::importFragment() |
| { |
| BiffInputStream& rStrm = getInputStream(); |
| if( rStrm.startNextRecord() && (rStrm.getRecId() == BIFF_ID_PCDEFINITION) ) |
| { |
| // read PCDEFINITION and optional PCDEFINITION2 records |
| mrPivotCache.importPCDefinition( rStrm ); |
| |
| // read cache fields as long as another PCDFIELD record can be found |
| while( lclSeekToPCDField( rStrm ) ) |
| mrPivotCache.createCacheField( true ).importPCDField( rStrm ); |
| |
| // finalize the cache (check source range etc.) |
| mrPivotCache.finalizeImport(); |
| |
| // load the cache records, if the cache is based on a deleted or an external worksheet |
| if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() ) |
| { |
| /* Last call of lclSeekToPCDField() failed and kept stream position |
| unchanged. Stream should point to source data table now. */ |
| sal_Int16 nSheet = mrPivotCache.getSourceRange().Sheet; |
| WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), SHEETTYPE_WORKSHEET, nSheet ); |
| if( xSheetGlob.get() ) |
| { |
| BiffPivotCacheRecordsContext aContext( *xSheetGlob, mrPivotCache ); |
| while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) ) |
| aContext.importRecord( rStrm ); |
| } |
| } |
| } |
| |
| return rStrm.getRecId() == BIFF_ID_EOF; |
| } |
| |
| // ============================================================================ |
| |
| BiffPivotCacheRecordsContext::BiffPivotCacheRecordsContext( const WorksheetHelper& rHelper, const PivotCache& rPivotCache ) : |
| BiffWorksheetContextBase( rHelper ), |
| mrPivotCache( rPivotCache ), |
| mnColIdx( 0 ), |
| mnRowIdx( 0 ), |
| mbHasShared( false ), |
| mbInRow( false ) |
| { |
| // prepare sheet: insert column header names into top row |
| mrPivotCache.writeSourceHeaderCells( *this ); |
| |
| // find all fields without shared items, remember column indexes in source data |
| for( sal_Int32 nFieldIdx = 0, nFieldCount = mrPivotCache.getCacheFieldCount(), nCol = 0; nFieldIdx < nFieldCount; ++nFieldIdx ) |
| { |
| const PivotCacheField* pCacheField = mrPivotCache.getCacheField( nFieldIdx ); |
| if( pCacheField && pCacheField->isDatabaseField() ) |
| { |
| if( pCacheField->hasSharedItems() ) |
| mbHasShared = true; |
| else |
| maUnsharedCols.push_back( nCol ); |
| ++nCol; |
| } |
| } |
| } |
| |
| void BiffPivotCacheRecordsContext::importRecord( BiffInputStream& rStrm ) |
| { |
| if( rStrm.getRecId() == BIFF_ID_PCITEM_INDEXLIST ) |
| { |
| OSL_ENSURE( mbHasShared, "BiffPivotCacheRecordsContext::importRecord - unexpected PCITEM_INDEXLIST record" ); |
| // PCITEM_INDEXLIST record always in front of a new data row |
| startNextRow(); |
| mrPivotCache.importPCItemIndexList( rStrm, *this, mnRowIdx ); |
| mbInRow = !maUnsharedCols.empty(); // mbInRow remains true, if unshared items are expected |
| return; |
| } |
| |
| PivotCacheItem aItem; |
| switch( rStrm.getRecId() ) |
| { |
| case BIFF_ID_PCITEM_MISSING: break; |
| case BIFF_ID_PCITEM_STRING: aItem.readString( rStrm, *this ); break; |
| case BIFF_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break; |
| case BIFF_ID_PCITEM_INTEGER: aItem.readInteger( rStrm ); break; |
| case BIFF_ID_PCITEM_DATE: aItem.readDate( rStrm ); break; |
| case BIFF_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break; |
| case BIFF_ID_PCITEM_ERROR: aItem.readError( rStrm ); break; |
| default: return; // unknown record, ignore |
| } |
| |
| // find next column index, might start new row if no fields with shared items exist |
| if( mbInRow && (mnColIdx == maUnsharedCols.size()) ) |
| { |
| OSL_ENSURE( !mbHasShared, "BiffPivotCacheRecordsContext::importRecord - PCITEM_INDEXLIST record missing" ); |
| mbInRow = mbHasShared; // do not leave current row if PCITEM_INDEXLIST is expected |
| } |
| // start next row on first call, or on row wrap without shared items |
| if( !mbInRow ) |
| startNextRow(); |
| |
| // write the item data to the sheet cell |
| OSL_ENSURE( mnColIdx < maUnsharedCols.size(), "BiffPivotCacheRecordsContext::importRecord - invalid column index" ); |
| if( mnColIdx < maUnsharedCols.size() ) |
| mrPivotCache.writeSourceDataCell( *this, maUnsharedCols[ mnColIdx ], mnRowIdx, aItem ); |
| ++mnColIdx; |
| } |
| |
| void BiffPivotCacheRecordsContext::startNextRow() |
| { |
| mnColIdx = 0; |
| ++mnRowIdx; |
| mbInRow = true; |
| } |
| |
| // ============================================================================ |
| |
| } // namespace xls |
| } // namespace oox |