diff options
Diffstat (limited to 'sc/source/filter/oox/pivottablebuffer.cxx')
-rw-r--r-- | sc/source/filter/oox/pivottablebuffer.cxx | 1480 |
1 files changed, 1480 insertions, 0 deletions
diff --git a/sc/source/filter/oox/pivottablebuffer.cxx b/sc/source/filter/oox/pivottablebuffer.cxx new file mode 100644 index 000000000..3aadd5860 --- /dev/null +++ b/sc/source/filter/oox/pivottablebuffer.cxx @@ -0,0 +1,1480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <pivottablebuffer.hxx> + +#include <set> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sheet/CellFlags.hpp> +#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotFieldReference.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> +#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> +#include <com/sun/star/sheet/GeneralFunction.hpp> +#include <com/sun/star/sheet/XDataPilotDataLayoutFieldSupplier.hpp> +#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp> +#include <com/sun/star/sheet/XSheetOperation.hpp> +#include <com/sun/star/xml/sax/XFastAttributeList.hpp> + +#include <comphelper/sequence.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <oox/helper/binaryinputstream.hxx> +#include <oox/helper/attributelist.hxx> +#include <oox/helper/containerhelper.hxx> +#include <oox/helper/propertyset.hxx> +#include <oox/token/properties.hxx> +#include <oox/token/tokens.hxx> +#include <addressconverter.hxx> +#include <biffhelper.hxx> + +#include <dapiuno.hxx> +#include <dpobject.hxx> +#include <dpsave.hxx> +#include <dpdimsave.hxx> +#include <document.hxx> +#include <documentimport.hxx> +#include <workbooksettings.hxx> + +namespace oox::xls { + +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sheet; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::uno; + +namespace { + +const sal_Int32 OOX_PT_DATALAYOUTFIELD = -2; /// Placeholder index of data layout field. + +const sal_Int32 OOX_PT_PREVIOUS_ITEM = 0x001000FC; /// Calculation of data item result is based on previous item. +const sal_Int32 OOX_PT_NEXT_ITEM = 0x001000FD; /// Calculation of data item result is based on next item. + +const sal_uInt32 BIFF12_PTFIELD_DATAFIELD = 0x00000008; +const sal_uInt32 BIFF12_PTFIELD_DEFAULT = 0x00000100; +const sal_uInt32 BIFF12_PTFIELD_SUM = 0x00000200; +const sal_uInt32 BIFF12_PTFIELD_COUNTA = 0x00000400; +const sal_uInt32 BIFF12_PTFIELD_AVERAGE = 0x00000800; +const sal_uInt32 BIFF12_PTFIELD_MAX = 0x00001000; +const sal_uInt32 BIFF12_PTFIELD_MIN = 0x00002000; +const sal_uInt32 BIFF12_PTFIELD_PRODUCT = 0x00004000; +const sal_uInt32 BIFF12_PTFIELD_COUNT = 0x00008000; +const sal_uInt32 BIFF12_PTFIELD_STDDEV = 0x00010000; +const sal_uInt32 BIFF12_PTFIELD_STDDEVP = 0x00020000; +const sal_uInt32 BIFF12_PTFIELD_VAR = 0x00040000; +const sal_uInt32 BIFF12_PTFIELD_VARP = 0x00080000; + +const sal_uInt32 BIFF12_PTFIELD_SHOWALL = 0x00000020; +const sal_uInt32 BIFF12_PTFIELD_OUTLINE = 0x00000040; +const sal_uInt32 BIFF12_PTFIELD_INSERTBLANKROW = 0x00000080; +const sal_uInt32 BIFF12_PTFIELD_SUBTOTALTOP = 0x00000100; +const sal_uInt32 BIFF12_PTFIELD_INSERTPAGEBREAK = 0x00000800; +const sal_uInt32 BIFF12_PTFIELD_AUTOSORT = 0x00001000; +const sal_uInt32 BIFF12_PTFIELD_SORTASCENDING = 0x00002000; +const sal_uInt32 BIFF12_PTFIELD_AUTOSHOW = 0x00004000; +const sal_uInt32 BIFF12_PTFIELD_AUTOSHOWTOP = 0x00008000; +const sal_uInt32 BIFF12_PTFIELD_MULTIPAGEITEMS = 0x00080000; + +const sal_uInt16 BIFF12_PTFITEM_HIDDEN = 0x0001; +const sal_uInt16 BIFF12_PTFITEM_HIDEDETAILS = 0x0002; + +const sal_uInt8 BIFF12_PTPAGEFIELD_HASNAME = 0x01; +const sal_Int32 BIFF12_PTPAGEFIELD_MULTIITEMS = 0x001000FE; + +const sal_uInt16 BIFF12_PTFILTER_HASNAME = 0x0001; +const sal_uInt16 BIFF12_PTFILTER_HASDESCRIPTION = 0x0002; +const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE1 = 0x0004; +const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE2 = 0x0008; + +const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01; +const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02; + +const sal_uInt32 BIFF12_PTDEF_SHOWITEMS = 0x00000100; +const sal_uInt32 BIFF12_PTDEF_DISABLEFIELDLIST = 0x00000400; +const sal_uInt32 BIFF12_PTDEF_HIDECALCMEMBERS = 0x00001000; +const sal_uInt32 BIFF12_PTDEF_WITHHIDDENTOTALS = 0x00002000; +const sal_uInt32 BIFF12_PTDEF_HIDEDRILL = 0x00100000; +const sal_uInt32 BIFF12_PTDEF_PRINTDRILL = 0x00200000; +const sal_uInt32 BIFF12_PTDEF_HIDEHEADERS = 0x80000000; + +const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYROW = 0x00000004; +const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYCOL = 0x00000008; +const sal_uInt32 BIFF12_PTDEF_ENABLEDRILL = 0x00000020; +const sal_uInt32 BIFF12_PTDEF_PRESERVEFORMATTING = 0x00000080; +const sal_uInt32 BIFF12_PTDEF_USEAUTOFORMAT = 0x00000100; +const sal_uInt32 BIFF12_PTDEF_SHOWERROR = 0x00000200; +const sal_uInt32 BIFF12_PTDEF_SHOWMISSING = 0x00000400; +const sal_uInt32 BIFF12_PTDEF_PAGEOVERTHENDOWN = 0x00000800; +const sal_uInt32 BIFF12_PTDEF_SUBTOTALHIDDENITEMS = 0x00001000; +const sal_uInt32 BIFF12_PTDEF_ROWGRANDTOTALS = 0x00002000; +const sal_uInt32 BIFF12_PTDEF_COLGRANDTOTALS = 0x00004000; +const sal_uInt32 BIFF12_PTDEF_FIELDPRINTTITLES = 0x00008000; +const sal_uInt32 BIFF12_PTDEF_ITEMPRINTTITLES = 0x00020000; +const sal_uInt32 BIFF12_PTDEF_MERGEITEM = 0x00040000; +const sal_uInt32 BIFF12_PTDEF_HASDATACAPTION = 0x00080000; +const sal_uInt32 BIFF12_PTDEF_HASGRANDTOTALCAPTION = 0x00100000; +const sal_uInt32 BIFF12_PTDEF_HASPAGESTYLE = 0x00200000; +const sal_uInt32 BIFF12_PTDEF_HASPIVOTTABLESTYLE = 0x00400000; +const sal_uInt32 BIFF12_PTDEF_HASVACATEDSTYLE = 0x00800000; +const sal_uInt32 BIFF12_PTDEF_APPLYNUMFMT = 0x01000000; +const sal_uInt32 BIFF12_PTDEF_APPLYFONT = 0x02000000; +const sal_uInt32 BIFF12_PTDEF_APPLYALIGNMENT = 0x04000000; +const sal_uInt32 BIFF12_PTDEF_APPLYBORDER = 0x08000000; +const sal_uInt32 BIFF12_PTDEF_APPLYFILL = 0x10000000; +const sal_uInt32 BIFF12_PTDEF_APPLYPROTECTION = 0x20000000; +const sal_uInt32 BIFF12_PTDEF_HASTAG = 0x40000000; + +const sal_uInt32 BIFF12_PTDEF_NOERRORCAPTION = 0x00000040; +const sal_uInt32 BIFF12_PTDEF_NOMISSINGCAPTION = 0x00000080; +const sal_uInt32 BIFF12_PTDEF_HASROWHEADERCAPTION = 0x00000400; +const sal_uInt32 BIFF12_PTDEF_HASCOLHEADERCAPTION = 0x00000800; +const sal_uInt32 BIFF12_PTDEF_FIELDLISTSORTASC = 0x00001000; +const sal_uInt32 BIFF12_PTDEF_NOCUSTOMLISTSORT = 0x00004000; + +const sal_uInt8 BIFF12_PTDEF_ROWAXIS = 1; +const sal_uInt8 BIFF12_PTDEF_COLAXIS = 2; + +} // namespace + +PTFieldItemModel::PTFieldItemModel() : + mnCacheItem( -1 ), + mnType( XML_data ), + mbShowDetails( true ), + mbHidden( false ) +{ +} + +void PTFieldItemModel::setBiffType( sal_uInt16 nType ) +{ + static const sal_Int32 spnTypes[] = { XML_data, XML_default, + XML_sum, XML_countA, XML_avg, XML_max, XML_min, XML_product, XML_count, + XML_stdDev, XML_stdDevP, XML_var, XML_varP, XML_grand, XML_blank }; + mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_data ); +} + +PTFieldModel::PTFieldModel() : + mnAxis( XML_TOKEN_INVALID ), + mnNumFmtId( 0 ), + mnAutoShowItems( 10 ), + mnAutoShowRankBy( -1 ), + mnSortType( XML_manual ), + mnSortRefField( -1 ), + mnSortRefItem( -1 ), + mbDataField( false ), + mbDefaultSubtotal( true ), + mbSumSubtotal( false ), + mbCountASubtotal( false ), + mbAverageSubtotal( false ), + mbMaxSubtotal( false ), + mbMinSubtotal( false ), + mbProductSubtotal( false ), + mbCountSubtotal( false ), + mbStdDevSubtotal( false ), + mbStdDevPSubtotal( false ), + mbVarSubtotal( false ), + mbVarPSubtotal( false ), + mbShowAll( true ), + mbOutline( true ), + mbSubtotalTop( true ), + mbInsertBlankRow( false ), + mbInsertPageBreak( false ), + mbAutoShow( false ), + mbTopAutoShow( true ), + mbMultiPageItems( false ) +{ +} + +void PTFieldModel::setBiffAxis( sal_uInt8 nAxis ) +{ + /* Weird. The axis field is organized as bit field, but only one of the + row/col/page flags are allowed at the same time and refer to the values + 'axisRow', 'axisCol', and 'axisPage' of the XML attribute + 'pivotField@axis'. Additionally, the fourth bit determines if the field + is a data field, which may appear combined with the row/col/page flags. + Therefore, this bit is unrelated to the 'axisValues' value of the + 'pivotField@axis' attribute, but refers to the 'pivotField@dataField' + boolean attribute. */ + static const sal_Int32 spnAxisIds[] = { XML_TOKEN_INVALID, XML_axisRow, XML_axisCol, XML_TOKEN_INVALID, XML_axisPage }; + mnAxis = STATIC_ARRAY_SELECT( spnAxisIds, nAxis, XML_TOKEN_INVALID ); +} + +PTPageFieldModel::PTPageFieldModel() : + mnField( -1 ), + mnItem( BIFF12_PTPAGEFIELD_MULTIITEMS ) +{ +} + +PTDataFieldModel::PTDataFieldModel() : + mnField( -1 ), + mnSubtotal( XML_sum ), + mnShowDataAs( XML_normal ), + mnBaseField( -1 ), + mnBaseItem( -1 ), + mnNumFmtId( 0 ) +{ +} + +void PTDataFieldModel::setBiffSubtotal( sal_Int32 nSubtotal ) +{ + static const sal_Int32 spnSubtotals[] = { XML_sum, XML_count, XML_average, XML_max, XML_min, XML_product, XML_countNums, XML_stdDev, XML_stdDevp, XML_var, XML_varp }; + mnSubtotal = STATIC_ARRAY_SELECT( spnSubtotals, nSubtotal, XML_TOKEN_INVALID ); +} + +void PTDataFieldModel::setBiffShowDataAs( sal_Int32 nShowDataAs ) +{ + static const sal_Int32 spnShowDataAs[] = { XML_normal, XML_difference, XML_percent, XML_percentDiff, XML_runTotal, XML_percentOfRow, XML_percentOfCol, XML_percentOfTotal, XML_index }; + mnShowDataAs = STATIC_ARRAY_SELECT( spnShowDataAs, nShowDataAs, XML_TOKEN_INVALID ); +} + +PivotTableField::PivotTableField( PivotTable& rPivotTable, sal_Int32 nFieldIndex ) : + WorkbookHelper( rPivotTable ), + mrPivotTable( rPivotTable ), + mnFieldIndex( nFieldIndex ) +{ +} + +void PivotTableField::importPivotField( const AttributeList& rAttribs ) +{ + /* The documentation mentions a value 'axisValues' for the attribute + 'pivotField@axis'. But this value is not used to mark a data field, as + data fields may be inserted in one of the row/column/page dimensions at + the same time. Therefore, the boolean attribute 'pivotField@dataField' + is really used to mark data fields. */ + maModel.mnAxis = rAttribs.getToken( XML_axis, XML_TOKEN_INVALID ); + maModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 ); + maModel.mnAutoShowItems = rAttribs.getInteger( XML_itemPageCount, 10 ); + maModel.mnAutoShowRankBy = rAttribs.getInteger( XML_rankBy, -1 ); + maModel.mnSortType = rAttribs.getToken( XML_sortType, XML_manual ); + maModel.mbDataField = rAttribs.getBool( XML_dataField, false ); + maModel.mbDefaultSubtotal = rAttribs.getBool( XML_defaultSubtotal, true ); + maModel.mbSumSubtotal = rAttribs.getBool( XML_sumSubtotal, false ); + maModel.mbCountASubtotal = rAttribs.getBool( XML_countASubtotal, false ); + maModel.mbAverageSubtotal = rAttribs.getBool( XML_avgSubtotal, false ); + maModel.mbMaxSubtotal = rAttribs.getBool( XML_maxSubtotal, false ); + maModel.mbMinSubtotal = rAttribs.getBool( XML_minSubtotal, false ); + maModel.mbProductSubtotal = rAttribs.getBool( XML_productSubtotal, false ); + maModel.mbCountSubtotal = rAttribs.getBool( XML_countSubtotal, false ); + maModel.mbStdDevSubtotal = rAttribs.getBool( XML_stdDevSubtotal, false ); + maModel.mbStdDevPSubtotal = rAttribs.getBool( XML_stdDevPSubtotal, false ); + maModel.mbVarSubtotal = rAttribs.getBool( XML_varSubtotal, false ); + maModel.mbVarPSubtotal = rAttribs.getBool( XML_varPSubtotal, false ); + maModel.mbShowAll = rAttribs.getBool( XML_showAll, true ); + maModel.mbOutline = rAttribs.getBool( XML_outline, true ); + maModel.mbSubtotalTop = rAttribs.getBool( XML_subtotalTop, true ); + maModel.mbInsertBlankRow = rAttribs.getBool( XML_insertBlankRow, false ); + maModel.mbInsertPageBreak = rAttribs.getBool( XML_insertPageBreak, false ); + maModel.mbAutoShow = rAttribs.getBool( XML_autoShow, false ); + maModel.mbTopAutoShow = rAttribs.getBool( XML_topAutoShow, true ); + maModel.mbMultiPageItems = rAttribs.getBool( XML_multipleItemSelectionAllowed, false ); +} + +void PivotTableField::importItem( const AttributeList& rAttribs ) +{ + PTFieldItemModel aModel; + aModel.mnCacheItem = rAttribs.getInteger( XML_x, -1 ); + aModel.mnType = rAttribs.getToken( XML_t, XML_data ); + aModel.mbShowDetails = rAttribs.getBool( XML_sd, true ); + aModel.mbHidden = rAttribs.getBool( XML_h, false ); + aModel.msCaption = rAttribs.getXString( XML_n, OUString() ); + maItems.push_back( aModel ); +} + +void PivotTableField::importReference( const AttributeList& rAttribs ) +{ + // field index is stored as unsigned integer + maModel.mnSortRefField = static_cast< sal_Int32 >( rAttribs.getUnsigned( XML_field, SAL_MAX_UINT32 ) ); +} + +void PivotTableField::importReferenceItem( const AttributeList& rAttribs ) +{ + maModel.mnSortRefItem = rAttribs.getInteger( XML_v, -1 ); +} + +void PivotTableField::importPTField( SequenceInputStream& rStrm ) +{ + sal_uInt32 nFlags1, nFlags2; + nFlags1 = rStrm.readuInt32(); + maModel.mnNumFmtId = rStrm.readInt32(); + nFlags2 = rStrm.readuInt32(); + maModel.mnAutoShowItems = rStrm.readInt32(); + maModel.mnAutoShowRankBy = rStrm.readInt32(); + + maModel.setBiffAxis( extractValue< sal_uInt8 >( nFlags1, 0, 3 ) ); + maModel.mbDataField = getFlag( nFlags1, BIFF12_PTFIELD_DATAFIELD ); + maModel.mbDefaultSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_DEFAULT ); + maModel.mbSumSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_SUM ); + maModel.mbCountASubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNTA ); + maModel.mbAverageSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_AVERAGE ); + maModel.mbMaxSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MAX ); + maModel.mbMinSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MIN ); + maModel.mbProductSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_PRODUCT ); + maModel.mbCountSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNT ); + maModel.mbStdDevSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEV ); + maModel.mbStdDevPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEVP ); + maModel.mbVarSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VAR ); + maModel.mbVarPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VARP ); + + maModel.mbShowAll = getFlag( nFlags2, BIFF12_PTFIELD_SHOWALL ); + maModel.mbOutline = getFlag( nFlags2, BIFF12_PTFIELD_OUTLINE ); + maModel.mbSubtotalTop = getFlag( nFlags2, BIFF12_PTFIELD_SUBTOTALTOP ); + maModel.mbInsertBlankRow = getFlag( nFlags2, BIFF12_PTFIELD_INSERTBLANKROW ); + maModel.mbInsertPageBreak = getFlag( nFlags2, BIFF12_PTFIELD_INSERTPAGEBREAK ); + maModel.mbAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOW ); + maModel.mbTopAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOWTOP ); + maModel.mbMultiPageItems = getFlag( nFlags2, BIFF12_PTFIELD_MULTIPAGEITEMS ); + + bool bAutoSort = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSORT ); + bool bAscending = getFlag( nFlags2, BIFF12_PTFIELD_SORTASCENDING ); + maModel.mnSortType = bAutoSort ? (bAscending ? XML_ascending : XML_descending) : XML_manual; +} + +void PivotTableField::importPTFItem( SequenceInputStream& rStrm ) +{ + PTFieldItemModel aModel; + sal_uInt8 nType; + sal_uInt16 nFlags; + nType = rStrm.readuChar(); + nFlags = rStrm.readuInt16(); + aModel.mnCacheItem = rStrm.readInt32(); + + aModel.setBiffType( nType ); + aModel.mbShowDetails = !getFlag( nFlags, BIFF12_PTFITEM_HIDEDETAILS ); + aModel.mbHidden = getFlag( nFlags, BIFF12_PTFITEM_HIDDEN ); + + maItems.push_back( aModel ); +} + +void PivotTableField::importPTReference( SequenceInputStream& rStrm ) +{ + maModel.mnSortRefField = rStrm.readInt32(); +} + +void PivotTableField::importPTReferenceItem( SequenceInputStream& rStrm ) +{ + maModel.mnSortRefItem = rStrm.readInt32(); +} + +void PivotTableField::finalizeImport( const Reference< XDataPilotDescriptor >& rxDPDesc ) +{ + /* Process all fields based on source data, other fields (e.g. group + fields) are processed from here. PivotCache::getCacheDatabaseIndex() + returns -1 for all fields not based on source data. */ + Reference< XDataPilotField > xDPField; + sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex ); + if( !((nDatabaseIdx >= 0) && rxDPDesc.is()) ) + return; + + try + { + // try to get the source field and its name from passed DataPilot descriptor + Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW ); + xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW ); + Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW ); + maDPFieldName = xDPFieldName->getName(); + OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeImport - no field name in source data found" ); + + // try to convert grouping settings + if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) ) + { + // numeric grouping is done inplace, no nested group fields will appear + if( pCacheField->hasNumericGrouping() ) + { + pCacheField->convertNumericGrouping( xDPField ); + } + else if( pCacheField->hasDateGrouping() ) + { + // first date group settings are inplace + pCacheField->createDateGroupField( xDPField ); + // create all nested group fields (if any) + mrPivotTable.finalizeDateGroupingImport( xDPField, mnFieldIndex ); + } + else if( pCacheField->hasParentGrouping() ) + { + + // create a list of all item names, needed to map between original and group items + ::std::vector< OUString > aItems; + pCacheField->getCacheItemNames( aItems ); + PivotCacheGroupItemVector aItemNames; + for( const auto& rItem : aItems ) + aItemNames.emplace_back( rItem ); + // create all nested group fields (if any) + mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, aItemNames ); + } + } + } + catch( Exception& ) + { + } +} + +void PivotTableField::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx ) +{ + if( maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken + { + if( PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) ) + { + if( !pCacheField->isDatabaseField() && pCacheField->hasDateGrouping() && (pCacheField->getGroupBaseField() == nBaseFieldIdx) ) + { + maDPFieldName = pCacheField->createDateGroupField( rxBaseDPField ); + pCacheField->setFinalGroupName(maDPFieldName); + OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeDateGroupingImport - cannot create date group field" ); + } + } + } +} + +void PivotTableField::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames ) +{ + if( !maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken + return; + + PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ); + if( !pCacheField ) + return; + + // data field can have user defined groupname captions, apply them + // if they do + IdCaptionPairList captionList; + for( const auto& rItem : maItems ) + { + if ( rItem.mnType == XML_data && rItem.msCaption.getLength() ) + captionList.emplace_back( rItem.mnCacheItem, rItem.msCaption ); + } + if ( !captionList.empty() ) + pCacheField->applyItemCaptions( captionList ); + + maDPFieldName = pCacheField->createParentGroupField( rxBaseDPField, rBaseCacheField, orItemNames ); + pCacheField->setFinalGroupName(maDPFieldName); + // on success, try to create nested group fields + Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName ); + if( xDPField.is() ) + mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, orItemNames ); +} + +void PivotTableField::finalizeImportBasedOnCache( const Reference< XDataPilotDescriptor >& rxDPDesc) +{ + /* Process all fields based on source data, other fields (e.g. group + fields) are processed based on cache fields.*/ + Reference< XDataPilotField > xDPField; + sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex ); + if( (nDatabaseIdx >= 0) && rxDPDesc.is() ) try + { + // Try to get the source field and its name from passed DataPilot descriptor + Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW ); + xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW ); + Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW ); + maDPFieldName = xDPFieldName->getName(); + SAL_WARN_IF( maDPFieldName.isEmpty(), "sc.filter", "PivotTableField::finalizeImportBasedOnCache - no field name in source data found" ); + } + catch( Exception& ) + { + } + + // Use group names already generated for another table using the same group field. + if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) ) + { + if(!pCacheField->getFinalGroupName().isEmpty()) + maDPFieldName = pCacheField->getFinalGroupName(); + } +} + +void PivotTableField::convertRowField() +{ + convertRowColPageField( XML_axisRow ); +} + +void PivotTableField::convertColField() +{ + convertRowColPageField( XML_axisCol ); +} + +void PivotTableField::convertHiddenField() +{ + convertRowColPageField( XML_TOKEN_INVALID ); +} + +void PivotTableField::convertPageField( const PTPageFieldModel& rPageField ) +{ + OSL_ENSURE( rPageField.mnField == mnFieldIndex, "PivotTableField::convertPageField - wrong field index" ); + // convert all settings common for row/column/page fields + Reference< XDataPilotField > xDPField = convertRowColPageField( XML_axisPage ); + + if( !xDPField.is() ) + return; + + PropertySet aPropSet( xDPField ); + + // find cache item used as 'selected page' + sal_Int32 nCacheItem = -1; + if( maModel.mbMultiPageItems ) + { + // multiple items may be selected + OSL_ENSURE( rPageField.mnItem == BIFF12_PTPAGEFIELD_MULTIITEMS, "PivotTableField::convertPageField - unexpected cache item index" ); + // try to find a single visible item + bool bHasMultiItems = false; + for( const auto& rItem : maItems ) + { + if( (rItem.mnType == XML_data) && !rItem.mbHidden ) + { + bHasMultiItems = nCacheItem >= 0; + nCacheItem = bHasMultiItems ? -1 : rItem.mnCacheItem; + } + + if( bHasMultiItems ) + break; + } + } + else + { + // single item may be selected + if( (0 <= rPageField.mnItem) && (o3tl::make_unsigned(rPageField.mnItem) < maItems.size()) ) + nCacheItem = maItems[ rPageField.mnItem ].mnCacheItem; + } + + if( nCacheItem < 0 ) + return; + + if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) ) + { + if( const PivotCacheItem* pSharedItem = pCacheField->getCacheItem( nCacheItem ) ) + { + ScDPObject* pDPObj = mrPivotTable.getDPObject(); + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName()); + OUString aSelectedPage = pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate())); + aPropSet.setProperty( PROP_SelectedPage, aSelectedPage ); + } + } +} + +void PivotTableField::convertDataField( const PTDataFieldModel& rDataField ) +{ + OSL_ENSURE( rDataField.mnField == mnFieldIndex, "PivotTableField::convertDataField - wrong field index" ); + OSL_ENSURE( maModel.mbDataField, "PivotTableField::convertDataField - not a data field" ); + Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName ); + if( !xDPField.is() ) + return; + + PropertySet aPropSet( xDPField ); + + // field orientation + aPropSet.setProperty( PROP_Orientation, DataPilotFieldOrientation_DATA ); + + if (!rDataField.maName.isEmpty()) + aPropSet.setProperty(PROP_Name, rDataField.maName); + + /* Field aggregation function. Documentation is a little bit confused + about which names to use for the count functions. The name 'count' + means 'count all', and 'countNum' means 'count numbers'. On the + other hand, for subtotals, 'countA' means 'count all', and 'count' + means 'count numbers' (see above). */ + GeneralFunction eAggFunc = GeneralFunction_SUM; + switch( rDataField.mnSubtotal ) + { + case XML_sum: eAggFunc = GeneralFunction_SUM; break; + case XML_count: eAggFunc = GeneralFunction_COUNT; break; + case XML_average: eAggFunc = GeneralFunction_AVERAGE; break; + case XML_max: eAggFunc = GeneralFunction_MAX; break; + case XML_min: eAggFunc = GeneralFunction_MIN; break; + case XML_product: eAggFunc = GeneralFunction_PRODUCT; break; + case XML_countNums: eAggFunc = GeneralFunction_COUNTNUMS; break; + case XML_stdDev: eAggFunc = GeneralFunction_STDEV; break; + case XML_stdDevp: eAggFunc = GeneralFunction_STDEVP; break; + case XML_var: eAggFunc = GeneralFunction_VAR; break; + case XML_varp: eAggFunc = GeneralFunction_VARP; break; + default: OSL_FAIL( "PivotTableField::convertDataField - unknown aggregation function" ); + } + aPropSet.setProperty( PROP_Function, eAggFunc ); + + // field reference ('show data as') + DataPilotFieldReference aReference; + aReference.ReferenceType = DataPilotFieldReferenceType::NONE; + switch( rDataField.mnShowDataAs ) + { + case XML_difference: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_DIFFERENCE; break; + case XML_percent: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE; break; + case XML_percentDiff: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE; break; + case XML_runTotal: aReference.ReferenceType = DataPilotFieldReferenceType::RUNNING_TOTAL; break; + case XML_percentOfRow: aReference.ReferenceType = DataPilotFieldReferenceType::ROW_PERCENTAGE; break; + case XML_percentOfCol: aReference.ReferenceType = DataPilotFieldReferenceType::COLUMN_PERCENTAGE; break; + case XML_percentOfTotal: aReference.ReferenceType = DataPilotFieldReferenceType::TOTAL_PERCENTAGE; break; + case XML_index: aReference.ReferenceType = DataPilotFieldReferenceType::INDEX; break; + } + if( aReference.ReferenceType == DataPilotFieldReferenceType::NONE ) + return; + + const PivotCacheField* pCacheField = mrPivotTable.getCacheField( rDataField.mnBaseField ); + if( !pCacheField ) + return; + + aReference.ReferenceField = pCacheField->getName(); + switch( rDataField.mnBaseItem ) + { + case OOX_PT_PREVIOUS_ITEM: + aReference.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS; + break; + case OOX_PT_NEXT_ITEM: + aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT; + break; + default: + aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED; + if( const PivotCacheItem* pCacheItem = pCacheField->getCacheItem( rDataField.mnBaseItem ) ) + aReference.ReferenceItemName = pCacheItem->getName(); + } + aPropSet.setProperty( PROP_Reference, aReference ); +} + +// private -------------------------------------------------------------------- + +Reference< XDataPilotField > PivotTableField::convertRowColPageField( sal_Int32 nAxis ) +{ + bool bDataLayout = mnFieldIndex == OOX_PT_DATALAYOUTFIELD; + Reference< XDataPilotField > xDPField = bDataLayout ? mrPivotTable.getDataLayoutField() : mrPivotTable.getDataPilotField( maDPFieldName ); + OSL_ENSURE( bDataLayout || (nAxis == maModel.mnAxis), "PivotTableField::convertRowColPageField - field axis mismatch" ); + + if( xDPField.is() ) + { + // TODO: Use this to set properties directly, bypassing the slow uno layer. + ScDPObject* pDPObj = mrPivotTable.getDPObject(); + + PropertySet aPropSet( xDPField ); + + // field orientation + DataPilotFieldOrientation eFieldOrient = DataPilotFieldOrientation_HIDDEN; + switch( nAxis ) + { + case XML_axisRow: eFieldOrient = DataPilotFieldOrientation_ROW; break; + case XML_axisCol: eFieldOrient = DataPilotFieldOrientation_COLUMN; break; + case XML_axisPage: eFieldOrient = DataPilotFieldOrientation_PAGE; break; + } + if( eFieldOrient != DataPilotFieldOrientation_HIDDEN ) + aPropSet.setProperty( PROP_Orientation, eFieldOrient ); + + // all other settings not for the data layout field + if( !bDataLayout ) + { + /* Field subtotal functions. Ignore the 'defaultSubtotal' flag, if + explicit functions are set. This is different behaviour between + XML (where 'defaultSubtotal' is set regardless of other + functions) and binary formats (where 'defaultSubtotal' is not + set if other functions are set). */ + ::std::vector< GeneralFunction > aSubtotals; + /* Order of subtotals is fixed in Excel. Documentation is a little + bit confused about which names to use for the count functions. + For subtotals, 'countA' means 'count all', and 'count' means + 'count numbers'. On the other hand, for the data field + aggregation function, 'count' means 'count all', and 'countNum' + means 'count numbers' (see below). */ + if( maModel.mbSumSubtotal ) aSubtotals.push_back( GeneralFunction_SUM ); + if( maModel.mbCountASubtotal ) aSubtotals.push_back( GeneralFunction_COUNT ); + if( maModel.mbAverageSubtotal ) aSubtotals.push_back( GeneralFunction_AVERAGE ); + if( maModel.mbMaxSubtotal ) aSubtotals.push_back( GeneralFunction_MAX ); + if( maModel.mbMinSubtotal ) aSubtotals.push_back( GeneralFunction_MIN ); + if( maModel.mbProductSubtotal ) aSubtotals.push_back( GeneralFunction_PRODUCT ); + if( maModel.mbCountSubtotal ) aSubtotals.push_back( GeneralFunction_COUNTNUMS ); + if( maModel.mbStdDevSubtotal ) aSubtotals.push_back( GeneralFunction_STDEV ); + if( maModel.mbStdDevPSubtotal ) aSubtotals.push_back( GeneralFunction_STDEVP ); + if( maModel.mbVarSubtotal ) aSubtotals.push_back( GeneralFunction_VAR ); + if( maModel.mbVarPSubtotal ) aSubtotals.push_back( GeneralFunction_VARP ); + // if no function is set manually, check the 'defaultSubtotal' flag + if( aSubtotals.empty() && maModel.mbDefaultSubtotal ) + aSubtotals.push_back( GeneralFunction_AUTO ); + aPropSet.setProperty( PROP_Subtotals, comphelper::containerToSequence( aSubtotals ) ); + + // layout settings + DataPilotFieldLayoutInfo aLayoutInfo; + aLayoutInfo.LayoutMode = maModel.mbOutline ? + (maModel.mbSubtotalTop ? DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP : DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM) : + DataPilotFieldLayoutMode::TABULAR_LAYOUT; + aLayoutInfo.AddEmptyLines = maModel.mbInsertBlankRow; + aPropSet.setProperty( PROP_LayoutInfo, aLayoutInfo ); + aPropSet.setProperty( PROP_ShowEmpty, maModel.mbShowAll ); + + // auto show (OOXML/BIFF12 only) + if( maModel.mbAutoShow ) + { + DataPilotFieldAutoShowInfo aAutoShowInfo; + aAutoShowInfo.IsEnabled = true; + aAutoShowInfo.ShowItemsMode = maModel.mbTopAutoShow ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM; + aAutoShowInfo.ItemCount = maModel.mnAutoShowItems; + if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnAutoShowRankBy ) ) + aAutoShowInfo.DataField = pCacheField->getName(); + aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo ); + } + + // auto sort + DataPilotFieldSortInfo aSortInfo; + aSortInfo.IsAscending = maModel.mnSortType == XML_ascending; + if( (maModel.mnSortType != XML_ascending) && (maModel.mnSortType != XML_descending) ) + { + aSortInfo.Mode = DataPilotFieldSortMode::MANUAL; + } + else + { + const PivotCacheField* pCacheField = (maModel.mnSortRefField == OOX_PT_DATALAYOUTFIELD) ? + mrPivotTable.getCacheFieldOfDataField( maModel.mnSortRefItem ) : nullptr; + if( pCacheField ) + { + aSortInfo.Mode = DataPilotFieldSortMode::DATA; + aSortInfo.Field = pCacheField->getName(); + } + else + { + aSortInfo.Mode = DataPilotFieldSortMode::NAME; + } + } + aPropSet.setProperty( PROP_SortInfo, aSortInfo ); + + // item settings + if (const PivotCacheField* pCacheField = mrPivotTable.getCacheField(mnFieldIndex)) + { + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName()); + SAL_WARN_IF(!pDim, "sc.filter", "PivotTableField::convertRowColPageField - no Dimension found for: " << pCacheField->getName()); + + if (pDim) try + { + for( const auto& rItem : maItems ) + { + if (rItem.mnType != XML_data) + continue; + + const PivotCacheItem* pSharedItem = pCacheField->getCacheItem(rItem.mnCacheItem); + if (!pSharedItem) + continue; + + try + { + ScDPSaveMember* pMem = pDim->GetMemberByName(pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate()))); + pMem->SetShowDetails(rItem.mbShowDetails); + pMem->SetIsVisible(!rItem.mbHidden); + } + catch( Exception& ) + { + // catch every failed container access to be able to process following items + } + } + } + catch (const Exception&) {} + } + } + } + return xDPField; +} + +PTFilterModel::PTFilterModel() : + mfValue( 0.0 ), + mnField( -1 ), + mnMemPropField( -1 ), + mnType( XML_TOKEN_INVALID ), + mnEvalOrder( 0 ), + mnId( -1 ), + mnMeasureField( -1 ), + mnMeasureHier( -1 ), + mbTopFilter( true ) +{ +} + +PivotTableFilter::PivotTableFilter( const PivotTable& rPivotTable ) : + WorkbookHelper( rPivotTable ), + mrPivotTable( rPivotTable ) +{ +} + +void PivotTableFilter::importFilter( const AttributeList& rAttribs ) +{ + maModel.maName = rAttribs.getXString( XML_name, OUString() ); + maModel.maDescription = rAttribs.getXString( XML_description, OUString() ); + maModel.maStrValue1 = rAttribs.getXString( XML_stringValue1, OUString() ); + maModel.maStrValue2 = rAttribs.getXString( XML_stringValue2, OUString() ); + maModel.mnField = rAttribs.getInteger( XML_fld, -1 ); + maModel.mnMemPropField = rAttribs.getInteger( XML_mpFld, -1 ); + maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID ); + maModel.mnEvalOrder = rAttribs.getInteger( XML_evalOrder, 0 ); + maModel.mnId = rAttribs.getInteger( XML_id, -1 ); + maModel.mnMeasureField = rAttribs.getInteger( XML_iMeasureFld, -1 ); + maModel.mnMeasureHier = rAttribs.getInteger( XML_iMeasureHier, -1 ); +} + +void PivotTableFilter::importTop10( const AttributeList& rAttribs ) +{ + OSL_ENSURE( rAttribs.getBool( XML_percent, false ) == (maModel.mnType == XML_percent), + "PivotTableFilter::importTop10 - unexpected value of percent attribute" ); + maModel.mfValue = rAttribs.getDouble( XML_val, 0.0 ); + maModel.mbTopFilter = rAttribs.getBool( XML_top, true ); +} + +void PivotTableFilter::importPTFilter( SequenceInputStream& rStrm ) +{ + sal_Int32 nType; + sal_uInt16 nFlags; + maModel.mnField = rStrm.readInt32(); + maModel.mnMemPropField = rStrm.readInt32(); + nType = rStrm.readInt32(); + rStrm.skip( 4 ); // unused + maModel.mnId = rStrm.readInt32(); + maModel.mnMeasureField = rStrm.readInt32(); + maModel.mnMeasureHier = rStrm.readInt32(); + nFlags = rStrm.readuInt16(); + if( getFlag( nFlags, BIFF12_PTFILTER_HASNAME ) ) + rStrm >> maModel.maName; + if( getFlag( nFlags, BIFF12_PTFILTER_HASDESCRIPTION ) ) + rStrm >> maModel.maDescription; + if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE1 ) ) + rStrm >> maModel.maStrValue1; + if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE2 ) ) + rStrm >> maModel.maStrValue2; + + static const sal_Int32 spnTypes[] = + { + XML_unknown, + // data field top10 filter (1-3) + XML_count, XML_percent, XML_sum, + // caption filter (4-17) + XML_captionEqual, XML_captionNotEqual, + XML_captionBeginsWith, XML_captionNotBeginsWith, XML_captionEndsWith, XML_captionNotEndsWith, + XML_captionContains, XML_captionNotContains, XML_captionGreaterThan, XML_captionGreaterThanOrEqual, + XML_captionLessThan, XML_captionLessThanOrEqual, XML_captionBetween, XML_captionNotBetween, + // value filter (18-25) + XML_valueEqual, XML_valueNotEqual, XML_valueGreaterThan, XML_valueGreaterThanOrEqual, + XML_valueLessThan, XML_valueLessThanOrEqual, XML_valueBetween, XML_valueNotBetween, + // date filter (26-65) + XML_dateEqual, XML_dateOlderThan, XML_dateNewerThan, XML_dateBetween, + XML_tomorrow, XML_today, XML_yesterday, XML_nextWeek, XML_thisWeek, XML_lastWeek, + XML_nextMonth, XML_thisMonth, XML_lastMonth, XML_nextQuarter, XML_thisQuarter, XML_lastQuarter, + XML_nextYear, XML_thisYear, XML_lastYear, XML_yearToDate, XML_Q1, XML_Q2, XML_Q3, XML_Q4, + XML_M1, XML_M2, XML_M3, XML_M4, XML_M5, XML_M6, XML_M7, XML_M8, XML_M9, XML_M10, XML_M11, XML_M12, + XML_dateNotEqual, XML_dateOlderThanOrEqual, XML_dateNewerThanOrEqual, XML_dateNotBetween + }; + maModel.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID ); +} + +void PivotTableFilter::importTop10Filter( SequenceInputStream& rStrm ) +{ + sal_uInt8 nFlags; + nFlags = rStrm.readuChar(); + maModel.mfValue = rStrm.readDouble(); + + SAL_WARN_IF( + getFlag(nFlags, BIFF12_TOP10FILTER_PERCENT) != (maModel.mnType == XML_percent), + "sc.filter", + "PivotTableFilter::importTop10 - unexpected value of percent attribute"); + maModel.mbTopFilter = getFlag( nFlags, BIFF12_TOP10FILTER_TOP ); +} + +void PivotTableFilter::finalizeImport() +{ + // only simple top10 filter supported + if( maModel.mnType != XML_count ) + return; + + PropertySet aPropSet( mrPivotTable.getDataPilotField( maModel.mnField ) ); + if( aPropSet.is() ) + { + DataPilotFieldAutoShowInfo aAutoShowInfo; + aAutoShowInfo.IsEnabled = true; + aAutoShowInfo.ShowItemsMode = maModel.mbTopFilter ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM; + aAutoShowInfo.ItemCount = getLimitedValue< sal_Int32, double >( maModel.mfValue, 0, SAL_MAX_INT32 ); + if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnMeasureField ) ) + aAutoShowInfo.DataField = pCacheField->getName(); + aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo ); + } +} + +PTDefinitionModel::PTDefinitionModel() : + mnCacheId( -1 ), + mnDataPosition( 0 ), + mnPageWrap( 0 ), + mnIndent( 1 ), + mnChartFormat( 0 ), + mbDataOnRows( false ), + mbShowError( false ), + mbShowMissing( true ), + mbShowItems( true ), + mbDisableFieldList( false ), + mbShowCalcMembers( true ), + mbVisualTotals( true ), + mbShowDrill( true ), + mbPrintDrill( false ), + mbEnableDrill( true ), + mbPreserveFormatting( true ), + mbUseAutoFormat( false ), + mbPageOverThenDown( false ), + mbSubtotalHiddenItems( false ), + mbRowGrandTotals( true ), + mbColGrandTotals( true ), + mbFieldPrintTitles( false ), + mbItemPrintTitles( false ), + mbMergeItem( false ), + mbShowEmptyRow( false ), + mbShowEmptyCol( false ), + mbShowHeaders( true ), + mbFieldListSortAsc( false ), + mbCustomListSort( true ) +{ +} + +PTLocationModel::PTLocationModel() : + mnFirstHeaderRow( 0 ), + mnFirstDataRow( 0 ), + mnFirstDataCol( 0 ), + mnRowPageCount( 0 ), + mnColPageCount( 0 ) +{ +} + +PivotTable::PivotTable( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mpDPObject(nullptr), + maDataField( *this, OOX_PT_DATALAYOUTFIELD ), + mpPivotCache( nullptr ) +{ +} + +void PivotTable::importPivotTableDefinition( const AttributeList& rAttribs ) +{ + maDefModel.maName = rAttribs.getXString( XML_name, OUString() ); + maDefModel.maDataCaption = rAttribs.getXString( XML_dataCaption , OUString() ); + maDefModel.maGrandTotalCaption = rAttribs.getXString( XML_grandTotalCaption, OUString() ); + maDefModel.maRowHeaderCaption = rAttribs.getXString( XML_rowHeaderCaption, OUString() ); + maDefModel.maColHeaderCaption = rAttribs.getXString( XML_colHeaderCaption, OUString() ); + maDefModel.maErrorCaption = rAttribs.getXString( XML_errorCaption, OUString() ); + maDefModel.maMissingCaption = rAttribs.getXString( XML_missingCaption, OUString() ); + maDefModel.maPageStyle = rAttribs.getXString( XML_pageStyle, OUString() ); + maDefModel.maPivotTableStyle = rAttribs.getXString( XML_pivotTableStyle, OUString() ); + maDefModel.maVacatedStyle = rAttribs.getXString( XML_vacatedStyle, OUString() ); + maDefModel.maTag = rAttribs.getXString( XML_tag, OUString() ); + maDefModel.mnCacheId = rAttribs.getInteger( XML_cacheId, -1 ); + maDefModel.mnDataPosition = rAttribs.getInteger( XML_dataPosition, 0 ); + maDefModel.mnPageWrap = rAttribs.getInteger( XML_pageWrap, 0 ); + maDefModel.mnIndent = rAttribs.getInteger( XML_indent, 1 ); + maDefModel.mnChartFormat = rAttribs.getInteger( XML_chartFormat, 0 ); + maDefModel.mnAutoFormatId = rAttribs.getInteger( XML_autoFormatId, 0 ); + maDefModel.mbDataOnRows = rAttribs.getBool( XML_dataOnRows, false ); + maDefModel.mbShowError = rAttribs.getBool( XML_showError, false ); + maDefModel.mbShowMissing = rAttribs.getBool( XML_showMissing, true ); + maDefModel.mbShowItems = rAttribs.getBool( XML_showItems, true ); + maDefModel.mbDisableFieldList = rAttribs.getBool( XML_disableFieldList, false ); + maDefModel.mbShowCalcMembers = rAttribs.getBool( XML_showCalcMbrs, true ); + maDefModel.mbVisualTotals = rAttribs.getBool( XML_visualTotals, true ); + maDefModel.mbShowDrill = rAttribs.getBool( XML_showDrill, true ); + maDefModel.mbPrintDrill = rAttribs.getBool( XML_printDrill, false ); + maDefModel.mbEnableDrill = rAttribs.getBool( XML_enableDrill, true ); + maDefModel.mbPreserveFormatting = rAttribs.getBool( XML_preserveFormatting, true ); + maDefModel.mbUseAutoFormat = rAttribs.getBool( XML_useAutoFormatting, false ); + maDefModel.mbPageOverThenDown = rAttribs.getBool( XML_pageOverThenDown, false ); + maDefModel.mbSubtotalHiddenItems = rAttribs.getBool( XML_subtotalHiddenItems, false ); + maDefModel.mbRowGrandTotals = rAttribs.getBool( XML_rowGrandTotals, true ); + maDefModel.mbColGrandTotals = rAttribs.getBool( XML_colGrandTotals, true ); + maDefModel.mbFieldPrintTitles = rAttribs.getBool( XML_fieldPrintTitles, false ); + maDefModel.mbItemPrintTitles = rAttribs.getBool( XML_itemPrintTitles, false ); + maDefModel.mbMergeItem = rAttribs.getBool( XML_mergeItem, false ); + maDefModel.mbShowEmptyRow = rAttribs.getBool( XML_showEmptyRow, false ); + maDefModel.mbShowEmptyCol = rAttribs.getBool( XML_showEmptyCol, false ); + maDefModel.mbShowHeaders = rAttribs.getBool( XML_showHeaders, true ); + maDefModel.mbFieldListSortAsc = rAttribs.getBool( XML_fieldListSortAscending, false ); + maDefModel.mbCustomListSort = rAttribs.getBool( XML_customListSort, true ); + maDefModel.mbApplyNumFmt = rAttribs.getBool( XML_applyNumberFormats, false ); + maDefModel.mbApplyFont = rAttribs.getBool( XML_applyFontFormats, false ); + maDefModel.mbApplyAlignment = rAttribs.getBool( XML_applyAlignmentFormats, false ); + maDefModel.mbApplyBorder = rAttribs.getBool( XML_applyBorderFormats, false ); + maDefModel.mbApplyFill = rAttribs.getBool( XML_applyPatternFormats, false ); + // OOXML and BIFF12 documentation differ: OOXML mentions width/height, BIFF12 mentions protection + maDefModel.mbApplyProtection = rAttribs.getBool( XML_applyWidthHeightFormats, false ); +} + +void PivotTable::importLocation( const AttributeList& rAttribs, sal_Int16 nSheet ) +{ + AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet ); + maLocationModel.mnFirstHeaderRow = rAttribs.getInteger( XML_firstHeaderRow, 0 ); + maLocationModel.mnFirstDataRow = rAttribs.getInteger( XML_firstDataRow, 0 ); + maLocationModel.mnFirstDataCol = rAttribs.getInteger( XML_firstDataCol, 0 ); + maLocationModel.mnRowPageCount = rAttribs.getInteger( XML_rowPageCount, 0 ); + maLocationModel.mnColPageCount = rAttribs.getInteger( XML_colPageCount, 0 ); +} + +void PivotTable::importRowField( const AttributeList& rAttribs ) +{ + importField( maRowFields, rAttribs ); +} + +void PivotTable::importColField( const AttributeList& rAttribs ) +{ + importField( maColFields, rAttribs ); +} + +void PivotTable::importPageField( const AttributeList& rAttribs ) +{ + PTPageFieldModel aModel; + aModel.maName = rAttribs.getXString( XML_name, OUString() ); + aModel.mnField = rAttribs.getInteger( XML_fld, -1 ); + // specification is wrong, XML_item is not the cache item, but the field item + aModel.mnItem = rAttribs.getInteger( XML_item, BIFF12_PTPAGEFIELD_MULTIITEMS ); + maPageFields.push_back( aModel ); +} + +void PivotTable::importDataField( const AttributeList& rAttribs ) +{ + PTDataFieldModel aModel; + aModel.maName = rAttribs.getXString( XML_name, OUString() ); + aModel.mnField = rAttribs.getInteger( XML_fld, -1 ); + aModel.mnSubtotal = rAttribs.getToken( XML_subtotal, XML_sum ); + aModel.mnShowDataAs = rAttribs.getToken( XML_showDataAs, XML_normal ); + aModel.mnBaseField = rAttribs.getInteger( XML_baseField, -1 ); + aModel.mnBaseItem = rAttribs.getInteger( XML_baseItem, -1 ); + aModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 ); + maDataFields.push_back( aModel ); +} + +void PivotTable::putToInteropGrabBag(const OUString& sName, const AttributeList& rAttribs) +{ + if (auto xFastAttributeList = rAttribs.getFastAttributeList()) + { + // Store both known and unknown attribute sequences to the grab bag as is + css::uno::Sequence<css::xml::FastAttribute> aFast = xFastAttributeList->getFastAttributes(); + css::uno::Sequence<css::xml::Attribute> aUnk = xFastAttributeList->getUnknownAttributes(); + css::uno::Sequence<css::uno::Any> aVal{ css::uno::Any(aFast), css::uno::Any(aUnk) }; + maInteropGrabBag[sName] <<= aVal; + } +} + +void PivotTable::importPTDefinition( SequenceInputStream& rStrm ) +{ + sal_uInt32 nFlags1, nFlags2, nFlags3; + sal_uInt8 nDataAxis; + nFlags1 = rStrm.readuInt32(); + nFlags2 = rStrm.readuInt32(); + nFlags3 = rStrm.readuInt32(); + nDataAxis = rStrm.readuChar(); + maDefModel.mnPageWrap = rStrm.readuInt8(); + rStrm.skip( 2 ); // refresh versions + maDefModel.mnDataPosition = rStrm.readInt32(); + maDefModel.mnAutoFormatId = rStrm.readuInt16(); + rStrm.skip( 2 ); // unused + maDefModel.mnChartFormat = rStrm.readInt32(); + maDefModel.mnCacheId = rStrm.readInt32(); + rStrm >> maDefModel.maName; + if( getFlag( nFlags2, BIFF12_PTDEF_HASDATACAPTION ) ) + rStrm >> maDefModel.maDataCaption; + if( getFlag( nFlags2, BIFF12_PTDEF_HASGRANDTOTALCAPTION ) ) + rStrm >> maDefModel.maGrandTotalCaption; + if( !getFlag( nFlags3, BIFF12_PTDEF_NOERRORCAPTION ) ) // missing flag indicates existing string + rStrm >> maDefModel.maErrorCaption; + if( !getFlag( nFlags3, BIFF12_PTDEF_NOMISSINGCAPTION ) ) // missing flag indicates existing string + rStrm >> maDefModel.maMissingCaption; + if( getFlag( nFlags2, BIFF12_PTDEF_HASPAGESTYLE ) ) + rStrm >> maDefModel.maPageStyle; + if( getFlag( nFlags2, BIFF12_PTDEF_HASPIVOTTABLESTYLE ) ) + rStrm >> maDefModel.maPivotTableStyle; + if( getFlag( nFlags2, BIFF12_PTDEF_HASVACATEDSTYLE ) ) + rStrm >> maDefModel.maVacatedStyle; + if( getFlag( nFlags2, BIFF12_PTDEF_HASTAG ) ) + rStrm >> maDefModel.maTag; + if( getFlag( nFlags3, BIFF12_PTDEF_HASCOLHEADERCAPTION ) ) // TODO: right order (col/row)? spec is unclear + rStrm >> maDefModel.maColHeaderCaption; + if( getFlag( nFlags3, BIFF12_PTDEF_HASROWHEADERCAPTION ) ) + rStrm >> maDefModel.maRowHeaderCaption; + + SAL_WARN_IF( + (nDataAxis != BIFF12_PTDEF_ROWAXIS) && (nDataAxis != BIFF12_PTDEF_COLAXIS), + "sc.filter", + "PivotTable::importPTDefinition - unexpected axis position for data field"); + + maDefModel.mnIndent = extractValue< sal_uInt8 >( nFlags1, 24, 7 ); + maDefModel.mbDataOnRows = nDataAxis == BIFF12_PTDEF_ROWAXIS; + maDefModel.mbShowError = getFlag( nFlags2, BIFF12_PTDEF_SHOWERROR ); + maDefModel.mbShowMissing = getFlag( nFlags2, BIFF12_PTDEF_SHOWMISSING ); + maDefModel.mbShowItems = getFlag( nFlags1, BIFF12_PTDEF_SHOWITEMS ); + maDefModel.mbDisableFieldList = getFlag( nFlags1, BIFF12_PTDEF_DISABLEFIELDLIST ); + maDefModel.mbShowCalcMembers = !getFlag( nFlags1, BIFF12_PTDEF_HIDECALCMEMBERS ); + maDefModel.mbVisualTotals = !getFlag( nFlags1, BIFF12_PTDEF_WITHHIDDENTOTALS ); + maDefModel.mbShowDrill = !getFlag( nFlags1, BIFF12_PTDEF_HIDEDRILL ); + maDefModel.mbPrintDrill = getFlag( nFlags1, BIFF12_PTDEF_PRINTDRILL ); + maDefModel.mbEnableDrill = getFlag( nFlags2, BIFF12_PTDEF_ENABLEDRILL ); + maDefModel.mbPreserveFormatting = getFlag( nFlags2, BIFF12_PTDEF_PRESERVEFORMATTING ); + maDefModel.mbUseAutoFormat = getFlag( nFlags2, BIFF12_PTDEF_USEAUTOFORMAT ); + maDefModel.mbPageOverThenDown = getFlag( nFlags2, BIFF12_PTDEF_PAGEOVERTHENDOWN ); + maDefModel.mbSubtotalHiddenItems = getFlag( nFlags2, BIFF12_PTDEF_SUBTOTALHIDDENITEMS ); + maDefModel.mbRowGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_ROWGRANDTOTALS ); + maDefModel.mbColGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_COLGRANDTOTALS ); + maDefModel.mbFieldPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_FIELDPRINTTITLES ); + maDefModel.mbItemPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_ITEMPRINTTITLES ); + maDefModel.mbMergeItem = getFlag( nFlags2, BIFF12_PTDEF_MERGEITEM ); + maDefModel.mbApplyNumFmt = getFlag( nFlags2, BIFF12_PTDEF_APPLYNUMFMT ); + maDefModel.mbApplyFont = getFlag( nFlags2, BIFF12_PTDEF_APPLYFONT ); + maDefModel.mbApplyAlignment = getFlag( nFlags2, BIFF12_PTDEF_APPLYALIGNMENT ); + maDefModel.mbApplyBorder = getFlag( nFlags2, BIFF12_PTDEF_APPLYBORDER ); + maDefModel.mbApplyFill = getFlag( nFlags2, BIFF12_PTDEF_APPLYFILL ); + maDefModel.mbApplyProtection = getFlag( nFlags2, BIFF12_PTDEF_APPLYPROTECTION ); + maDefModel.mbShowEmptyRow = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYROW ); + maDefModel.mbShowEmptyCol = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYCOL ); + maDefModel.mbShowHeaders = !getFlag( nFlags1, BIFF12_PTDEF_HIDEHEADERS ); + maDefModel.mbFieldListSortAsc = getFlag( nFlags3, BIFF12_PTDEF_FIELDLISTSORTASC ); + maDefModel.mbCustomListSort = !getFlag( nFlags3, BIFF12_PTDEF_NOCUSTOMLISTSORT ); +} + +void PivotTable::importPTLocation( SequenceInputStream& rStrm, sal_Int16 nSheet ) +{ + BinRange aBinRange; + rStrm >> aBinRange; + maLocationModel.mnFirstHeaderRow = rStrm.readInt32(); + maLocationModel.mnFirstDataRow = rStrm.readInt32(); + maLocationModel.mnFirstDataCol = rStrm.readInt32(); + maLocationModel.mnRowPageCount = rStrm.readInt32(); + maLocationModel.mnColPageCount = rStrm.readInt32(); + AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, aBinRange, nSheet ); +} + +void PivotTable::importPTRowFields( SequenceInputStream& rStrm ) +{ + importFields( maRowFields, rStrm ); +} + +void PivotTable::importPTColFields( SequenceInputStream& rStrm ) +{ + importFields( maColFields, rStrm ); +} + +void PivotTable::importPTPageField( SequenceInputStream& rStrm ) +{ + PTPageFieldModel aModel; + sal_uInt8 nFlags; + aModel.mnField = rStrm.readInt32(); + aModel.mnItem = rStrm.readInt32(); + rStrm.skip( 4 ); // hierarchy + nFlags = rStrm.readuChar(); + if( getFlag( nFlags, BIFF12_PTPAGEFIELD_HASNAME ) ) + rStrm >> aModel.maName; + maPageFields.push_back( aModel ); +} + +void PivotTable::importPTDataField( SequenceInputStream& rStrm ) +{ + PTDataFieldModel aModel; + sal_Int32 nSubtotal, nShowDataAs; + sal_uInt8 nHasName; + aModel.mnField = rStrm.readInt32( ); + nSubtotal = rStrm.readInt32(); + nShowDataAs = rStrm.readInt32(); + aModel.mnBaseField = rStrm.readInt32(); + aModel.mnBaseItem = rStrm.readInt32(); + aModel.mnNumFmtId = rStrm.readInt32(); + nHasName = rStrm.readuChar(); + if( nHasName == 1 ) + rStrm >> aModel.maName; + aModel.setBiffSubtotal( nSubtotal ); + aModel.setBiffShowDataAs( nShowDataAs ); + maDataFields.push_back( aModel ); +} + +PivotTableField& PivotTable::createTableField() +{ + sal_Int32 nFieldIndex = static_cast< sal_Int32 >( maFields.size() ); + PivotTableFieldVector::value_type xTableField = std::make_shared<PivotTableField>( *this, nFieldIndex ); + maFields.push_back( xTableField ); + return *xTableField; +} + +PivotTableFilter& PivotTable::createTableFilter() +{ + PivotTableFilterVector::value_type xTableFilter = std::make_shared<PivotTableFilter>( *this ); + maFilters.push_back( xTableFilter ); + return *xTableFilter; +} + +void PivotTable::finalizeImport() +{ + if( !getAddressConverter().validateCellRange( maLocationModel.maRange, true, true ) ) + return; + + mpPivotCache = getPivotCaches().importPivotCacheFragment( maDefModel.mnCacheId ); + if( !mpPivotCache || !mpPivotCache->isValidDataSource() || maDefModel.maName.isEmpty() ) + return; + + // clear destination area of the original pivot table + try + { + Reference< XSheetOperation > xSheetOp( getCellRangeFromDoc( maLocationModel.maRange ), UNO_QUERY_THROW ); + using namespace ::com::sun::star::sheet::CellFlags; + xSheetOp->clearContents( VALUE | DATETIME | STRING | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED ); + } + catch( Exception& ) + { + } + + try + { + // create a new data pilot descriptor based on the source data + Reference< XDataPilotTablesSupplier > xDPTablesSupp( getSheetFromDoc( maLocationModel.maRange.aStart.Tab() ), UNO_QUERY_THROW ); + Reference< XDataPilotTables > xDPTables( xDPTablesSupp->getDataPilotTables(), UNO_SET_THROW ); + mxDPDescriptor.set( xDPTables->createDataPilotDescriptor(), UNO_SET_THROW ); + ScRange aRange = mpPivotCache->getSourceRange(); + CellRangeAddress aCellRangeAddress( aRange.aStart.Tab(), + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row() ); + mxDPDescriptor->setSourceRange( aCellRangeAddress ); + mxDPDescriptor->setTag( maDefModel.maTag ); + + // TODO: This is a hack. Eventually we need to convert the whole thing to the internal API. + auto pImpl = comphelper::getFromUnoTunnel<ScDataPilotDescriptorBase>(mxDPDescriptor); + if (!pImpl) + return; + + mpDPObject = pImpl->GetDPObject(); + if (!mpDPObject) + return; + + // global data pilot properties + PropertySet aDescProp( mxDPDescriptor ); + aDescProp.setProperty( PROP_ColumnGrand, maDefModel.mbColGrandTotals ); + aDescProp.setProperty( PROP_RowGrand, maDefModel.mbRowGrandTotals ); + aDescProp.setProperty( PROP_ShowFilterButton, false ); + aDescProp.setProperty( PROP_DrillDownOnDoubleClick, maDefModel.mbEnableDrill ); + + // finalize all fields, this finds field names and creates grouping fields + finalizeFieldsImport(); + + // all row fields + for( const auto& rRowField : maRowFields ) + if( PivotTableField* pField = getTableField( rRowField ) ) + pField->convertRowField(); + + // all column fields + for( const auto& rColField : maColFields ) + if( PivotTableField* pField = getTableField( rColField ) ) + pField->convertColField(); + + // all page fields + for( const auto& rPageField : maPageFields ) + if( PivotTableField* pField = getTableField( rPageField.mnField ) ) + pField->convertPageField( rPageField ); + + // all hidden fields + ::std::set< sal_Int32 > aVisFields; + aVisFields.insert( maRowFields.begin(), maRowFields.end() ); + aVisFields.insert( maColFields.begin(), maColFields.end() ); + for( const auto& rPageField : maPageFields ) + aVisFields.insert( rPageField.mnField ); + sal_Int32 nIndex = 0; + for( auto& rxField : maFields ) + { + if( aVisFields.count( nIndex ) == 0 ) + rxField->convertHiddenField(); + ++nIndex; + } + + // all data fields + for( auto& rDataField : maDataFields ) + { + if( const PivotCacheField* pCacheField = getCacheField( rDataField.mnField ) ) + { + if ( pCacheField-> getGroupBaseField() != -1 ) + rDataField.mnField = pCacheField-> getGroupBaseField(); + } + if( PivotTableField* pField = getTableField( rDataField.mnField ) ) + pField->convertDataField( rDataField ); + } + + // filters + maFilters.forEachMem( &PivotTableFilter::finalizeImport ); + + // calculate base position of table + CellAddress aPos( maLocationModel.maRange.aStart.Tab(), maLocationModel.maRange.aStart.Col(), maLocationModel.maRange.aStart.Row() ); + /* If page fields exist, include them into the destination + area (they are excluded in Excel). Add an extra blank row. */ + if( !maPageFields.empty() ) + aPos.Row = ::std::max< sal_Int32 >( static_cast< sal_Int32 >( aPos.Row - maPageFields.size() - 1 ), 0 ); + + // save interop grab bag + mpDPObject->PutInteropGrabBag(std::move(maInteropGrabBag)); + + // insert the DataPilot table into the sheet + xDPTables->insertNewByName( maDefModel.maName, aPos, mxDPDescriptor ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sc", "PivotTable::finalizeImport - exception while creating the DataPilot table" ); + } +} + +void PivotTable::finalizeFieldsImport() +{ + if (maFields.empty()) + return; + + /* Check whether group fields are already imported for another table + sharing the same groups. */ + ScDPObject* pDPObj = getDPObject(); + const ScDocument& rDoc = getDocImport().getDoc(); + if (rDoc.HasPivotTable()) + { + const ScDPCollection* pDPCollection = rDoc.GetDPCollection(); + assert(pDPCollection != nullptr); + const ScDPDimensionSaveData* pGroups = nullptr; + bool bRefFound = pDPCollection->GetReferenceGroups(*pDPObj, &pGroups); + // Apply reference groups on this table. + if (bRefFound && pGroups && pGroups->HasGroupDimensions()) { + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + if (pSaveData) { + pSaveData->SetDimensionData(pGroups); + pDPObj->ReloadGroupTableData(); + maFields.forEachMem(&PivotTableField::finalizeImportBasedOnCache, ::std::cref(mxDPDescriptor)); + return; + } + + } + } + maFields.forEachMem(&PivotTableField::finalizeImport, ::std::cref(mxDPDescriptor)); +} + +void PivotTable::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx ) +{ + // process all fields, there is no chaining information in the cache fields + maFields.forEachMem( &PivotTableField::finalizeDateGroupingImport, ::std::cref(rxBaseDPField), nBaseFieldIdx ); +} + +void PivotTable::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, + const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames ) +{ + // try to create parent group fields that group the items of the passed base field + if( PivotTableField* pParentTableField = maFields.get( rBaseCacheField.getParentGroupField() ).get() ) + pParentTableField->finalizeParentGroupingImport( rxBaseDPField, rBaseCacheField, orItemNames ); +} + +Reference< XDataPilotField > PivotTable::getDataPilotField( const OUString& rFieldName ) const +{ + Reference< XDataPilotField > xDPField; + if( !rFieldName.isEmpty() && mxDPDescriptor.is() ) try + { + Reference< XNameAccess > xDPFieldsNA( mxDPDescriptor->getDataPilotFields(), UNO_QUERY_THROW ); + xDPField.set( xDPFieldsNA->getByName( rFieldName ), UNO_QUERY ); + } + catch( Exception& ) + { + } + return xDPField; +} + +Reference< XDataPilotField > PivotTable::getDataPilotField( sal_Int32 nFieldIdx ) const +{ + Reference< XDataPilotField > xDPField; + if( const PivotTableField* pTableField = maFields.get( nFieldIdx ).get() ) + xDPField = getDataPilotField( pTableField->getDPFieldName() ); + return xDPField; +} + +Reference< XDataPilotField > PivotTable::getDataLayoutField() const +{ + Reference< XDataPilotField > xDPField; + try + { + Reference< XDataPilotDataLayoutFieldSupplier > xDPDataFieldSupp( mxDPDescriptor, UNO_QUERY_THROW ); + xDPField = xDPDataFieldSupp->getDataLayoutField(); + } + catch( Exception& ) + { + } + return xDPField; +} + +PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx ) +{ + return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr; +} + +const PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx ) const +{ + return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr; +} + +const PivotCacheField* PivotTable::getCacheFieldOfDataField( sal_Int32 nDataItemIdx ) const +{ + const PTDataFieldModel* pDataField = ContainerHelper::getVectorElement( maDataFields, nDataItemIdx ); + return pDataField ? getCacheField( pDataField->mnField ) : nullptr; +} + +sal_Int32 PivotTable::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const +{ + return mpPivotCache ? mpPivotCache->getCacheDatabaseIndex( nFieldIdx ) : -1; +} + +// private -------------------------------------------------------------------- + +PivotTableField* PivotTable::getTableField( sal_Int32 nFieldIdx ) +{ + return (nFieldIdx == OOX_PT_DATALAYOUTFIELD) ? &maDataField : maFields.get( nFieldIdx ).get(); +} + +void PivotTable::importField( IndexVector& orFields, const AttributeList& rAttribs ) +{ + orFields.push_back( rAttribs.getInteger( XML_x, -1 ) ); +} + +void PivotTable::importFields( IndexVector& orFields, SequenceInputStream& rStrm ) +{ + OSL_ENSURE( orFields.empty(), "PivotTable::importFields - multiple record instances" ); + orFields.clear(); + sal_Int32 nCount = rStrm.readInt32(); + OSL_ENSURE( 4 * nCount == rStrm.getRemaining(), "PivotTable::importFields - invalid field count" ); + nCount = static_cast< sal_Int32 >( rStrm.getRemaining() / 4 ); + for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx ) + orFields.push_back( rStrm.readInt32() ); +} + +PivotTableBuffer::PivotTableBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +PivotTable& PivotTableBuffer::createPivotTable() +{ + PivotTableVector::value_type xTable = std::make_shared<PivotTable>( *this ); + maTables.push_back( xTable ); + return *xTable; +} + +void PivotTableBuffer::finalizeImport() +{ + if(maTables.empty()) + return; + + // Create formula groups. This needs to be done before pivot tables, because + // import may lead to calling ScDPObject::GetSource(), which calls ScDPObject::CreateObjects(), + // which will ensure all the cells are not dirty, causing recalculation even though + // ScDPObject::GetSource() doesn't need it. And the recalculation is slower without formula + // groups set up. Fixing that properly seems quite complex, given that do-everything approach + // of ScDPObject, so at least ensure the calculation is efficient. + ScDocument& rDoc = getDocImport().getDoc(); + rDoc.RegroupFormulaCells( ScRange( 0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), rDoc.GetMaxTableNumber())); + + maTables.forEachMem( &PivotTable::finalizeImport ); +} + +} // namespace oox + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |