/* -*- 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 <comphelper/diagnose_ex.hxx>
#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 <sax/fastattribs.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>
#include <PivotTableFormat.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 ),
    mbCompact( false ),
    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.mbCompact         = maModel.mbSubtotalTop && maModel.mbOutline && rAttribs.getBool( XML_compact, 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 );
    }
    catch( Exception& )
    {
    }

    try
    {
        // try to convert grouping settings
        if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
        {
            maDPFieldName = pCacheField->getName();

            // 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 );
            }
        }
        else
        {
            // No choice - check the sheet for field name
            Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW );
            maDPFieldName = xDPFieldName->getName();
            OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeImport - no field name in source data found" );
        }
    }
    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, Date( 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;
    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" );
          eAggFunc = GeneralFunction_SUM;
          break;
    }
    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;
            if (maModel.mbCompact)
            {
                aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::COMPACT_LAYOUT;
            }
            else if (maModel.mbOutline)
            {
                if (maModel.mbSubtotalTop)
                    aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
                else
                    aLayoutInfo.LayoutMode = DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
            }
            else
            {
                aLayoutInfo.LayoutMode = 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, Date( 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,
                                                  getScDocument());
    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::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;
}

PivotTableFormat& PivotTable::createFormat()
{
    PivotTableFormatVector::value_type xFormat = std::make_shared<PivotTableFormat>(*this);
    maFormats.push_back(xFormat);
    return *xFormat;
}

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
        rtl::Reference< ScTableSheetObj > xDPTablesSupp( getSheetFromDoc( maLocationModel.maRange.aStart.Tab() ) );
        Reference< XDataPilotTables > xDPTables( xDPTablesSupp->getDataPilotTables(), UNO_SET_THROW );
        mxDPDescriptor = static_cast<ScDataPilotDescriptorBase*>( xDPTables->createDataPilotDescriptor().get() );
        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 );

        mpDPObject = mxDPDescriptor->GetDPObject();
        if (!mpDPObject)
            return;

        // global data pilot properties
        if (auto* pSaveData = mpDPObject->GetSaveData())
        {
            pSaveData->SetColumnGrand(maDefModel.mbColGrandTotals);
            pSaveData->SetRowGrand(maDefModel.mbRowGrandTotals);
            pSaveData->SetDrillDown(maDefModel.mbEnableDrill);
            pSaveData->SetFilterButton(false);
            pSaveData->SetExpandCollapse(maDefModel.mbShowDrill);
        }
        mpDPObject->SetHideHeader(maLocationModel.mnFirstHeaderRow == 0);
        // 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 );

        // formats
        for (auto& pFormat : maFormats)
            pFormat->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 );

        // set the pivot table style info
        mpDPObject->setStyleInfo(std::move(maStyleInfo));

        // insert the DataPilot table into the sheet
        ScDocument& rDoc = getDocImport().getDoc();
        rDoc.SetImportingXML(true);
        xDPTables->insertNewByName( maDefModel.maName, aPos, mxDPDescriptor );
        rDoc.SetImportingXML(false);
    }
    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
    {
        if (mxDPDescriptor)
            xDPField = mxDPDescriptor->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: */
