blob: 6f33fafa3e24f8a51e39edcc5df56663753704e1 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
#include "xepivot.hxx"
#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
#include <algorithm>
#include <math.h>
#include <rtl/math.hxx>
#include <tools/date.hxx>
#include <svl/zformat.hxx>
#include <sot/storage.hxx>
#include "document.hxx"
#include "dpobject.hxx"
#include "dpsave.hxx"
#include "dpdimsave.hxx"
#include "dpshttab.hxx"
#include "globstr.hrc"
#include "fapihelper.hxx"
#include "xestring.hxx"
#include "xelink.hxx"
using namespace ::oox;
using ::com::sun::star::sheet::DataPilotFieldOrientation;
using ::com::sun::star::sheet::DataPilotFieldOrientation_HIDDEN;
using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
using ::com::sun::star::sheet::GeneralFunction;
using ::com::sun::star::sheet::DataPilotFieldSortInfo;
using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
using ::com::sun::star::sheet::DataPilotFieldReference;
using ::rtl::OUString;
using ::rtl::OString;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// ============================================================================
// Pivot cache
// ============================================================================
namespace {
// constants to track occurence of specific data types
const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
/** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
static const sal_uInt16 spnPCItemFlags[] =
{ // STR DBL INT DAT
EXC_SXFIELD_DATA_NONE, //
EXC_SXFIELD_DATA_STR, // x
EXC_SXFIELD_DATA_INT, // x
EXC_SXFIELD_DATA_STR_INT, // x x
EXC_SXFIELD_DATA_DBL, // x
EXC_SXFIELD_DATA_STR_DBL, // x x
EXC_SXFIELD_DATA_INT, // x x
EXC_SXFIELD_DATA_STR_INT, // x x x
EXC_SXFIELD_DATA_DATE, // x
EXC_SXFIELD_DATA_DATE_STR, // x x
EXC_SXFIELD_DATA_DATE_NUM, // x x
EXC_SXFIELD_DATA_DATE_STR, // x x x
EXC_SXFIELD_DATA_DATE_NUM, // x x
EXC_SXFIELD_DATA_DATE_STR, // x x x
EXC_SXFIELD_DATA_DATE_NUM, // x x x
EXC_SXFIELD_DATA_DATE_STR // x x x x
};
} // namespace
// ----------------------------------------------------------------------------
XclExpPCItem::XclExpPCItem( const String& rText ) :
XclExpRecord( (rText.Len() > 0) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
mnTypeFlag( EXC_PCITEM_DATA_STRING )
{
if( rText.Len() )
SetText( rText );
else
SetEmpty();
}
XclExpPCItem::XclExpPCItem( double fValue ) :
XclExpRecord( EXC_ID_SXDOUBLE, 8 )
{
SetDouble( fValue );
mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
}
XclExpPCItem::XclExpPCItem( const DateTime& rDateTime ) :
XclExpRecord( EXC_ID_SXDATETIME, 8 )
{
SetDateTime( rDateTime );
mnTypeFlag = EXC_PCITEM_DATA_DATE;
}
XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
XclExpRecord( EXC_ID_SXINTEGER, 2 ),
mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
{
SetInteger( nValue );
}
XclExpPCItem::XclExpPCItem( bool bValue ) :
XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
mnTypeFlag( EXC_PCITEM_DATA_STRING )
{
SetBool( bValue );
}
// ----------------------------------------------------------------------------
bool XclExpPCItem::EqualsText( const String& rText ) const
{
return (rText.Len() == 0) ? IsEmpty() : (GetText() && (*GetText() == rText));
}
bool XclExpPCItem::EqualsDouble( double fValue ) const
{
return GetDouble() && (*GetDouble() == fValue);
}
bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
{
return GetDateTime() && (*GetDateTime() == rDateTime);
}
bool XclExpPCItem::EqualsBool( bool bValue ) const
{
return GetBool() && (*GetBool() == bValue);
}
// ----------------------------------------------------------------------------
void XclExpPCItem::WriteBody( XclExpStream& rStrm )
{
if( const String* pText = GetText() )
{
rStrm << XclExpString( *pText );
}
else if( const double* pfValue = GetDouble() )
{
rStrm << *pfValue;
}
else if( const sal_Int16* pnValue = GetInteger() )
{
rStrm << *pnValue;
}
else if( const DateTime* pDateTime = GetDateTime() )
{
sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
sal_uInt16 nMonth = static_cast< sal_uInt16 >( pDateTime->GetMonth() );
sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
}
else if( const bool* pbValue = GetBool() )
{
rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
}
else
{
// nothing to do for SXEMPTY
DBG_ASSERT( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
}
}
// ============================================================================
XclExpPCField::XclExpPCField(
const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
const ScDPObject& rDPObj, const ScRange& rRange ) :
XclExpRecord( EXC_ID_SXFIELD ),
XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
XclExpRoot( rRoot ),
mrPCache( rPCache ),
mnTypeFlags( 0 )
{
// general settings for the standard field, insert all items from source range
InitStandardField( rRange );
// add special settings for inplace numeric grouping
if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
{
if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
{
if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
{
const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
DBG_ASSERT( !rNumInfo.Enable || !rDateInfo.Enable,
"XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
if( rNumInfo.Enable )
InitNumGroupField( rDPObj, rNumInfo );
else if( rDateInfo.Enable )
InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
}
}
}
// final settings (flags, item numbers)
Finalize();
}
XclExpPCField::XclExpPCField(
const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
XclExpRecord( EXC_ID_SXFIELD ),
XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
XclExpRoot( rRoot ),
mrPCache( rPCache ),
mnTypeFlags( 0 )
{
// add base field info (always using first base field, not predecessor of this field) ***
DBG_ASSERT( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
"XclExpPCField::FillFromGroup - wrong base cache field" );
maFieldInfo.maName = rGroupDim.GetGroupDimName();
maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
// add standard group info or date group info
const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
if( rDateInfo.Enable && (rGroupDim.GetDatePart() != 0) )
InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
else
InitStdGroupField( rBaseField, rGroupDim );
// final settings (flags, item numbers)
Finalize();
}
XclExpPCField::~XclExpPCField()
{
}
void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
{
DBG_ASSERT( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
"XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
}
sal_uInt16 XclExpPCField::GetItemCount() const
{
return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
}
const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
{
return GetVisItemList().GetRecord( nItemIdx ).get();
}
sal_uInt16 XclExpPCField::GetItemIndex( const String& rItemName ) const
{
const XclExpPCItemList& rItemList = GetVisItemList();
for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
return static_cast< sal_uInt16 >( nPos );
return EXC_PC_NOITEM;
}
sal_Size XclExpPCField::GetIndexSize() const
{
return Has16BitIndexes() ? 2 : 1;
}
void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
{
// only standard fields write item indexes
if( nSrcRow < maIndexVec.size() )
{
sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
if( Has16BitIndexes() )
rStrm << nIndex;
else
rStrm << static_cast< sal_uInt8 >( nIndex );
}
}
void XclExpPCField::Save( XclExpStream& rStrm )
{
DBG_ASSERT( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
// SXFIELD
XclExpRecord::Save( rStrm );
// SXFDBTYPE
XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
// list of grouping items
maGroupItemList.Save( rStrm );
// SXGROUPINFO
WriteSxgroupinfo( rStrm );
// SXNUMGROUP and additional grouping items (grouping limit settings)
WriteSxnumgroup( rStrm );
// list of original items
maOrigItemList.Save( rStrm );
}
// private --------------------------------------------------------------------
const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
{
DBG_ASSERT( IsStandardField() == maGroupItemList.IsEmpty(),
"XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
return IsStandardField() ? maOrigItemList : maGroupItemList;
}
void XclExpPCField::InitStandardField( const ScRange& rRange )
{
DBG_ASSERT( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
DBG_ASSERT( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
ScDocument& rDoc = GetDoc();
SvNumberFormatter& rFormatter = GetFormatter();
// field name is in top cell of the range
ScAddress aPos( rRange.aStart );
rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), maFieldInfo.maName );
// #i76047# maximum field name length in pivot cache is 255
maFieldInfo.maName.Erase( ::std::min( maFieldInfo.maName.Len(), EXC_PC_MAXSTRLEN ) );
// loop over all cells, create pivot cache items
for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
{
if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
{
double fValue = rDoc.GetValue( aPos );
short nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( aPos ) );
if( nFmtType == NUMBERFORMAT_LOGICAL )
InsertOrigBoolItem( fValue != 0 );
else if( nFmtType & NUMBERFORMAT_DATETIME )
InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ) );
else
InsertOrigDoubleItem( fValue );
}
else
{
String aText;
rDoc.GetString( aPos.Col(), aPos.Row(), aPos.Tab(), aText );
InsertOrigTextItem( aText );
}
}
}
void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
{
DBG_ASSERT( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
// loop over all groups of this field
for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
{
if( const ScDPSaveGroupItem* pGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx ) )
{
// the index of the new item containing the grouping name
sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
// loop over all elements of one group
for( size_t nElemIdx = 0, nElemCount = pGroupItem->GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
{
if( const String* pElemName = pGroupItem->GetElementByIndex( nElemIdx ) )
{
// try to find the item that is part of the group in the base field
sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
if( nBaseItemIdx < maFieldInfo.mnBaseItems )
{
// add group name item only if there are any valid base items
if( nGroupItemIdx == EXC_PC_NOITEM )
nGroupItemIdx = InsertGroupItem( new XclExpPCItem( pGroupItem->GetGroupName() ) );
maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
}
}
}
}
}
// add items and base item indexes of all ungrouped elements
for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
// items that are not part of a group still have the EXC_PC_NOITEM entry
if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
// try to find the base item
if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
// create a clone of the base item, insert its index into item order list
maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
}
void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
{
DBG_ASSERT( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
DBG_ASSERT( rNumInfo.Enable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
// new field type, date type, limit settings (min/max/step/auto)
if( rNumInfo.DateValues )
{
// special case: group by days with step count
meFieldType = EXC_PCFIELD_DATEGROUP;
maNumGroupInfo.SetScDateType( com::sun::star::sheet::DataPilotFieldGroupBy::DAYS );
SetDateGroupLimit( rNumInfo, true );
}
else
{
meFieldType = EXC_PCFIELD_NUMGROUP;
maNumGroupInfo.SetNumType();
SetNumGroupLimit( rNumInfo );
}
// generate visible items
InsertNumDateGroupItems( rDPObj, rNumInfo );
}
void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
{
DBG_ASSERT( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
DBG_ASSERT( rDateInfo.Enable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
// new field type
meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
// date type, limit settings (min/max/step/auto)
maNumGroupInfo.SetScDateType( nDatePart );
SetDateGroupLimit( rDateInfo, false );
// generate visible items
InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
}
void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
{
DBG_ASSERT( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
}
void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
{
size_t nItemIdx = maOrigItemList.GetSize();
maOrigItemList.AppendNewRecord( pNewItem );
InsertItemArrayIndex( nItemIdx );
mnTypeFlags |= pNewItem->GetTypeFlag();
}
void XclExpPCField::InsertOrigTextItem( const String& rText )
{
size_t nPos = 0;
bool bFound = false;
// #i76047# maximum item text length in pivot cache is 255
String aShortText( rText, 0, ::std::min( rText.Len(), EXC_PC_MAXSTRLEN ) );
for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) == true )
InsertItemArrayIndex( nPos );
if( !bFound )
InsertOrigItem( new XclExpPCItem( aShortText ) );
}
void XclExpPCField::InsertOrigDoubleItem( double fValue )
{
size_t nPos = 0;
bool bFound = false;
for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) == true )
InsertItemArrayIndex( nPos );
if( !bFound )
InsertOrigItem( new XclExpPCItem( fValue ) );
}
void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime )
{
size_t nPos = 0;
bool bFound = false;
for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) == true )
InsertItemArrayIndex( nPos );
if( !bFound )
InsertOrigItem( new XclExpPCItem( rDateTime ) );
}
void XclExpPCField::InsertOrigBoolItem( bool bValue )
{
size_t nPos = 0;
bool bFound = false;
for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) == true )
InsertItemArrayIndex( nPos );
if( !bFound )
InsertOrigItem( new XclExpPCItem( bValue ) );
}
sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
{
maGroupItemList.AppendNewRecord( pNewItem );
return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
}
void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
{
DBG_ASSERT( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
{
// get the string collection with original source elements
ScSheetDPData aDPData( GetDocPtr(), *pSrcDesc );
// Wang Xu Ming - DataPilot migration
// 2009-05-08
const std::vector< SCROW > aOrignial = aDPData.GetColumnEntries( static_cast< long >( GetBaseFieldIndex() ) );
// get the string collection with generated grouping elements
ScDPNumGroupDimension aTmpDim( rNumInfo );
if( nDatePart != 0 )
aTmpDim.MakeDateHelper( rNumInfo, nDatePart );
const std::vector< SCROW > aMemberIds = aTmpDim.GetNumEntries( static_cast< SCCOL >( GetBaseFieldIndex() ), aDPData.GetCacheTable().GetCache(), aOrignial );
for ( size_t nIdx = 0 ; nIdx < aMemberIds.size(); nIdx++ )
{
const ScDPItemData* pData = aDPData.GetMemberById( static_cast< long >( GetBaseFieldIndex() ) , aMemberIds[ nIdx] );
if ( pData )
InsertGroupItem( new XclExpPCItem( pData->GetString() ) );
}
// End Comments
}
}
void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
{
::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.AutoStart );
::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.AutoEnd );
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Start ) );
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.End ) );
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.Step ) );
}
void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
{
::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.AutoStart );
::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.AutoEnd );
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.Start ) ) );
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.End ) ) );
sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.Step, 1, SAL_MAX_INT16 ) : 1;
maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
}
void XclExpPCField::Finalize()
{
// flags
::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
// Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
/* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
for the current combination of item types is added to the flags. */
::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
// item count fields
maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
// maFieldInfo.mnBaseItems set in InitStdGroupField()
maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
}
void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
{
if( IsNumGroupField() || IsDateGroupField() )
{
// SXNUMGROUP record
rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
rStrm << maNumGroupInfo;
rStrm.EndRecord();
// limits (min/max/step) for numeric grouping
DBG_ASSERT( maNumGroupLimits.GetSize() == 3,
"XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
maNumGroupLimits.Save( rStrm );
}
}
void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
{
DBG_ASSERT( IsStdGroupField() != maGroupOrder.empty(),
"XclExpPCField::WriteSxgroupinfo - missing grouping info" );
if( IsStdGroupField() && !maGroupOrder.empty() )
{
rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
for( ScfUInt16Vec::const_iterator aIt = maGroupOrder.begin(), aEnd = maGroupOrder.end(); aIt != aEnd; ++aIt )
rStrm << *aIt;
rStrm.EndRecord();
}
}
void XclExpPCField::WriteBody( XclExpStream& rStrm )
{
rStrm << maFieldInfo;
}
// ============================================================================
XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
XclExpRoot( rRoot ),
mnListIdx( nListIdx ),
mbValid( false )
{
// source from sheet only
if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
{
/* maOrigSrcRange: Range received from the DataPilot object.
maExpSrcRange: Range written to the DCONREF record.
maDocSrcRange: Range used to get source data from Calc document.
This range may be shorter than maExpSrcRange to improve export
performance (#i22541#). */
maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->aSourceRange;
// internal sheet data only
SCTAB nScTab = maExpSrcRange.aStart.Tab();
if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) )
{
// ValidateRange() restricts source range to valid Excel limits
if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
{
// #i22541# skip empty cell areas (performance)
SCCOL nDocCol1, nDocCol2;
SCROW nDocRow1, nDocRow2;
GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
// #i22541# do not store index list for too big ranges
if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
// #160184# Excel must refresh tables to make drilldown working
::set_flag( maPCInfo.mnFlags, EXC_SXDB_REFRESH_LOAD );
// adjust row indexes, keep one row of empty area to surely have the empty cache item
if( nSrcRow1 < nDocRow1 )
nSrcRow1 = nDocRow1 - 1;
if( nSrcRow2 > nDocRow2 )
nSrcRow2 = nDocRow2 + 1;
maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
maDocSrcRange.aStart.SetRow( nSrcRow1 );
maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
maDocSrcRange.aEnd.SetRow( nSrcRow2 );
GetDoc().GetName( nScTab, maTabName );
maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
maPCInfo.mnStrmId = nListIdx + 1;
maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
AddFields( rDPObj );
mbValid = true;
}
}
}
}
bool XclExpPivotCache::HasItemIndexList() const
{
return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
}
sal_uInt16 XclExpPivotCache::GetFieldCount() const
{
return static_cast< sal_uInt16 >( maFieldList.GetSize() );
}
const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
{
return maFieldList.GetRecord( nFieldIdx ).get();
}
//UNUSED2009-05 const XclExpPCField* XclExpPivotCache::GetField( const String& rFieldName ) const
//UNUSED2009-05 {
//UNUSED2009-05 return const_cast< XclExpPivotCache* >( this )->GetFieldAcc( rFieldName );
//UNUSED2009-05 }
bool XclExpPivotCache::HasAddFields() const
{
// pivot cache can be shared, if there are no additional cache fields
return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
}
bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
{
/* For now, only sheet sources are supported, therefore it is enough to
compare the ScSheetSourceDesc. Later, there should be done more complicated
comparisons regarding the source type of rDPObj and this cache. */
if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
return pSrcDesc->aSourceRange == maOrigSrcRange;
return false;
}
void XclExpPivotCache::Save( XclExpStream& rStrm )
{
DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
// SXIDSTM
XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
// SXVS
XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
// DCONREF
WriteDconref( rStrm );
// create the pivot cache storage stream
WriteCacheStream();
}
void XclExpPivotCache::SaveXml( XclExpXmlStream& rStrm )
{
DBG_ASSERT( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
OUString sId = OUStringBuffer()
.appendAscii("rId")
.append( rStrm.GetUniqueIdOUString() )
.makeStringAndClear();
rWorkbook->startElement( XML_pivotCache,
XML_cacheId, OString::valueOf( (sal_Int32)maPCInfo.mnStrmId ).getStr(),
FSNS( XML_r, XML_id ), XclXmlUtils::ToOString( sId ).getStr(),
FSEND );
// SXIDSTM
XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).SaveXml( rStrm );
// SXVS
XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).SaveXml( rStrm );
// DCONREF
// OOXTODO: WriteDconref( rStrm );
// create the pivot cache storage stream
// OOXTODO: WriteCacheStream();
rWorkbook->endElement( XML_pivotCache );
}
// private --------------------------------------------------------------------
XclExpPCField* XclExpPivotCache::GetFieldAcc( sal_uInt16 nFieldIdx )
{
return maFieldList.GetRecord( nFieldIdx ).get();
}
XclExpPCField* XclExpPivotCache::GetFieldAcc( const String& rFieldName )
{
XclExpPCField* pField = 0;
for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
if( maFieldList.GetRecord( nPos )->GetFieldName() == rFieldName )
pField = maFieldList.GetRecord( nPos ).get();
return pField;
}
void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
{
AddStdFields( rDPObj );
maPCInfo.mnStdFields = GetFieldCount();
AddGroupFields( rDPObj );
AddCalcFields( rDPObj );
maPCInfo.mnTotalFields = GetFieldCount();
};
void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
{
// if item index list is not written, used shortened source range (maDocSrcRange) for performance
const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
// create a standard pivot cache field for each source column
for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
{
ScRange aColRange( rRange );
aColRange.aStart.SetCol( nScCol );
aColRange.aEnd.SetCol( nScCol );
maFieldList.AppendNewRecord( new XclExpPCField(
GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) );
}
}
void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
{
if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
{
if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
{
// loop over all existing standard fields to find their group fields
for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
{
if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) )
{
const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
XclExpPCField* pLastGroupField = pCurrStdField;
while( pGroupDim )
{
// insert the new grouping field
XclExpPCFieldRef xNewGroupField( new XclExpPCField(
GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
maFieldList.AppendRecord( xNewGroupField );
// register new grouping field at current grouping field, building a chain
pLastGroupField->SetGroupChildField( *xNewGroupField );
// next grouping dimension
pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
pLastGroupField = xNewGroupField.get();
}
}
}
}
}
}
void XclExpPivotCache::AddCalcFields( const ScDPObject& /*rDPObj*/ )
{
// not supported
}
void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
{
XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_STRING, &maTabName ) );
rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
<< static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
<< static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
<< static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
<< aRef
<< sal_uInt8( 0 );
rStrm.EndRecord();
}
void XclExpPivotCache::WriteCacheStream()
{
SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
if( xSvStrm.Is() )
{
XclExpStream aStrm( *xSvStrm, GetRoot() );
// SXDB
WriteSxdb( aStrm );
// SXDBEX
WriteSxdbex( aStrm );
// field list (SXFIELD and items)
maFieldList.Save( aStrm );
// index table (list of SXINDEXLIST)
WriteSxindexlistList( aStrm );
// EOF
XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
}
}
void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXDB, 21 );
rStrm << maPCInfo;
rStrm.EndRecord();
}
void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
rStrm << EXC_SXDBEX_CREATION_DATE
<< sal_uInt32( 0 ); // number of SXFORMULA records
rStrm.EndRecord();
}
void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
{
if( HasItemIndexList() )
{
sal_Size nRecSize = 0;
size_t nPos, nSize = maFieldList.GetSize();
for( nPos = 0; nPos < nSize; ++nPos )
nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
{
rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
for( nPos = 0; nPos < nSize; ++nPos )
maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
rStrm.EndRecord();
}
}
}
// ============================================================================
// Pivot table
// ============================================================================
namespace {
// ----------------------------------------------------------------------------
/** Returns a display string for a data field containing the field name and aggregation function. */
String lclGetDataFieldCaption( const String& rFieldName, GeneralFunction eFunc )
{
String aCaption;
sal_uInt16 nResIdx = 0;
using namespace ::com::sun::star::sheet;
switch( eFunc )
{
case GeneralFunction_SUM: nResIdx = STR_FUN_TEXT_SUM; break;
case GeneralFunction_COUNT: nResIdx = STR_FUN_TEXT_COUNT; break;
case GeneralFunction_AVERAGE: nResIdx = STR_FUN_TEXT_AVG; break;
case GeneralFunction_MAX: nResIdx = STR_FUN_TEXT_MAX; break;
case GeneralFunction_MIN: nResIdx = STR_FUN_TEXT_MIN; break;
case GeneralFunction_PRODUCT: nResIdx = STR_FUN_TEXT_PRODUCT; break;
case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT; break;
case GeneralFunction_STDEV: nResIdx = STR_FUN_TEXT_STDDEV; break;
case GeneralFunction_STDEVP: nResIdx = STR_FUN_TEXT_STDDEV; break;
case GeneralFunction_VAR: nResIdx = STR_FUN_TEXT_VAR; break;
case GeneralFunction_VARP: nResIdx = STR_FUN_TEXT_VAR; break;
default:;
}
if( nResIdx )
aCaption.Assign( ScGlobal::GetRscString( nResIdx ) ).AppendAscii( RTL_CONSTASCII_STRINGPARAM( " - " ) );
aCaption.Append( rFieldName );
return aCaption;
}
// ----------------------------------------------------------------------------
} // namespace
// ============================================================================
XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
XclExpRecord( EXC_ID_SXVI, 8 ),
mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
{
maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
maItemInfo.mnCacheIdx = nCacheIdx;
maItemInfo.maVisName.mbUseCache = mpCacheItem != 0;
}
XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) :
XclExpRecord( EXC_ID_SXVI, 8 ),
mpCacheItem( 0 )
{
maItemInfo.mnType = nItemType;
maItemInfo.mnCacheIdx = nCacheIdx;
maItemInfo.maVisName.mbUseCache = bUseCache;
}
const String& XclExpPTItem::GetItemName() const
{
return mpCacheItem ? mpCacheItem->ConvertToText() : EMPTY_STRING;
}
void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
{
// #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
// #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
// visible name
const OUString* pVisName = rSaveMem.GetLayoutName();
if (pVisName && !pVisName->equals(GetItemName()))
maItemInfo.SetVisName(*pVisName);
}
void XclExpPTItem::WriteBody( XclExpStream& rStrm )
{
rStrm << maItemInfo;
}
// ============================================================================
XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
mrPTable( rPTable ),
mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
{
maFieldInfo.mnCacheIdx = nCacheIdx;
// create field items
if( mpCacheField )
for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
}
// data access ----------------------------------------------------------------
const String& XclExpPTField::GetFieldName() const
{
return mpCacheField ? mpCacheField->GetFieldName() : EMPTY_STRING;
}
sal_uInt16 XclExpPTField::GetFieldIndex() const
{
// field index always equal to cache index
return maFieldInfo.mnCacheIdx;
}
sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
{
DBG_ASSERT( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
// will return 0xFFFF for empty vector -> ok
return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
}
//UNUSED2009-05 const XclExpPTItem* XclExpPTField::GetItem( const String& rName ) const
//UNUSED2009-05 {
//UNUSED2009-05 return const_cast< XclExpPTField* >( this )->GetItemAcc( rName );
//UNUSED2009-05 }
sal_uInt16 XclExpPTField::GetItemIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
{
for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
if( maItemList.GetRecord( nPos )->GetItemName() == rName )
return static_cast< sal_uInt16 >( nPos );
return nDefaultIdx;
}
// fill data --------------------------------------------------------------
/**
* Calc's subtotal names are escaped with backslashes ('\'), while Excel's
* are not escaped at all.
*/
static OUString lcl_convertCalcSubtotalName(const OUString& rName)
{
OUStringBuffer aBuf;
const sal_Unicode* p = rName.getStr();
sal_Int32 n = rName.getLength();
bool bEscaped = false;
for (sal_Int32 i = 0; i < n; ++i)
{
const sal_Unicode c = p[i];
if (!bEscaped && c == sal_Unicode('\\'))
{
bEscaped = true;
continue;
}
aBuf.append(c);
bEscaped = false;
}
return aBuf.makeStringAndClear();
}
void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
{
// orientation
DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
DBG_ASSERT( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
maFieldInfo.AddApiOrient( eOrient );
// show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
// visible name
const OUString* pLayoutName = rSaveDim.GetLayoutName();
if (pLayoutName && !pLayoutName->equals(GetFieldName()))
maFieldInfo.SetVisName(*pLayoutName);
const rtl::OUString* pSubtotalName = rSaveDim.GetSubtotalName();
if (pSubtotalName)
{
OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
maFieldExtInfo.mpFieldTotalName.reset(new rtl::OUString(aSubName));
}
// subtotals
XclPTSubtotalVec aSubtotals;
aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
maFieldInfo.SetSubtotals( aSubtotals );
// sorting
if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
{
maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA )
maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
}
// auto show
if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
{
::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
}
// layout
if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
{
maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
}
// special page field properties
if( eOrient == DataPilotFieldOrientation_PAGE )
{
maPageInfo.mnField = GetFieldIndex();
// selected item
if( rSaveDim.HasCurrentPage() )
maPageInfo.mnSelItem = GetItemIndex( rSaveDim.GetCurrentPage(), EXC_SXPI_ALLITEMS );
else
maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
}
// item properties
const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; i++)
if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) )
pItem->SetPropertiesFromMember( **i );
}
void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
{
maDataInfoVec.push_back( XclPTDataFieldInfo() );
XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
rDataInfo.mnField = GetFieldIndex();
// orientation
maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
// aggregation function
GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() );
rDataInfo.SetApiAggFunc( eFunc );
// visible name
const rtl::OUString* pVisName = rSaveDim.GetLayoutName();
if (pVisName)
rDataInfo.SetVisName(*pVisName);
else
rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
// result field reference
if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
{
rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
{
rDataInfo.mnRefField = pRefField->GetFieldIndex();
if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
}
}
}
void XclExpPTField::AppendSubtotalItems()
{
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
}
// records --------------------------------------------------------------------
void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
{
rStrm << maPageInfo;
}
void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
{
DBG_ASSERT( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
if( nDataInfoIdx < maDataInfoVec.size() )
{
rStrm.StartRecord( EXC_ID_SXDI, 12 );
rStrm << maDataInfoVec[ nDataInfoIdx ];
rStrm.EndRecord();
}
}
void XclExpPTField::Save( XclExpStream& rStrm )
{
// SXVD
WriteSxvd( rStrm );
// list of SXVI records
maItemList.Save( rStrm );
// SXVDEX
WriteSxvdex( rStrm );
}
// private --------------------------------------------------------------------
XclExpPTItem* XclExpPTField::GetItemAcc( const String& rName )
{
XclExpPTItem* pItem = 0;
for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
if( maItemList.GetRecord( nPos )->GetItemName() == rName )
pItem = maItemList.GetRecord( nPos ).get();
return pItem;
}
void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
{
maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) );
++maFieldInfo.mnItemCount;
}
void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXVD, 10 );
rStrm << maFieldInfo;
rStrm.EndRecord();
}
void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
rStrm << maFieldExtInfo;
rStrm.EndRecord();
}
// ============================================================================
XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
XclExpRoot( rRoot ),
mrPCache( rPCache ),
maDataOrientField( *this, EXC_SXIVD_DATA ),
mnOutScTab( 0 ),
mbValid( false ),
mbFilterBtn( false )
{
const ScRange& rOutScRange = rDPObj.GetOutRange();
if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
{
// DataPilot properties -----------------------------------------------
// pivot table properties from DP object
mnOutScTab = rOutScRange.aStart.Tab();
maPTInfo.maTableName = rDPObj.GetName();
maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
maPTViewEx9Info.Init( rDPObj );
if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
{
// additional properties from ScDPSaveData
SetPropertiesFromDP( *pSaveData );
// loop over all dimensions ---------------------------------------
/* 1) Default-construct all pivot table fields for all pivot cache fields. */
for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
const List& rDimList = pSaveData->GetDimensions();
sal_uLong nDimIdx, nDimCount = rDimList.Count();
/* 2) First process all data dimensions, they are needed for extended
settings of row/column/page fields (sorting/auto show). */
for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
if( pSaveDim->GetOrientation() == DataPilotFieldOrientation_DATA )
SetDataFieldPropertiesFromDim( *pSaveDim );
/* 3) Row/column/page/hidden fields. */
for( nDimIdx = 0; nDimIdx < nDimCount; ++nDimIdx )
if( const ScDPSaveDimension* pSaveDim = static_cast< const ScDPSaveDimension* >( rDimList.GetObject( nDimIdx ) ) )
if( pSaveDim->GetOrientation() != DataPilotFieldOrientation_DATA )
SetFieldPropertiesFromDim( *pSaveDim );
// Finalize -------------------------------------------------------
Finalize();
mbValid = true;
}
}
}
const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
{
return mrPCache.GetField( nCacheIdx );
}
const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
{
return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
}
const XclExpPTField* XclExpPivotTable::GetField( const String& rName ) const
{
return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
}
sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const String& rName, sal_uInt16 nDefaultIdx ) const
{
for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
if( const XclExpPTField* pField = GetField( aIt->first ) )
if( pField->GetFieldName() == rName )
return static_cast< sal_uInt16 >( aIt - maDataFields.begin() );
return nDefaultIdx;
}
void XclExpPivotTable::Save( XclExpStream& rStrm )
{
if( mbValid )
{
// SXVIEW
WriteSxview( rStrm );
// pivot table fields (SXVD, SXVDEX, and item records)
maFieldList.Save( rStrm );
// SXIVD records for row and column fields
WriteSxivd( rStrm, maRowFields );
WriteSxivd( rStrm, maColFields );
// SXPI
WriteSxpi( rStrm );
// list of SXDI records containing data field info
WriteSxdiList( rStrm );
// SXLI records
WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
// SXEX
WriteSxex( rStrm );
// QSISXTAG
WriteQsiSxTag( rStrm );
// SXVIEWEX9
WriteSxViewEx9( rStrm );
}
}
// private --------------------------------------------------------------------
XclExpPTField* XclExpPivotTable::GetFieldAcc( const String& rName )
{
XclExpPTField* pField = 0;
for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
pField = maFieldList.GetRecord( nPos ).get();
return pField;
}
XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
{
// data field orientation field?
if( rSaveDim.IsDataLayout() )
return &maDataOrientField;
// a real dimension
String aFieldName( rSaveDim.GetName() );
return aFieldName.Len() ? GetFieldAcc( aFieldName ) : 0;
}
// fill data --------------------------------------------------------------
void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
{
::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
mbFilterBtn = rSaveData.GetFilterButton();
const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
if (!pDim)
return;
const rtl::OUString* pLayoutName = pDim->GetLayoutName();
if (pLayoutName)
maPTInfo.maDataName = *pLayoutName;
else
maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
}
void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
{
if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
{
// field properties
pField->SetPropertiesFromDim( rSaveDim );
// update the corresponding field position list
DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
sal_uInt16 nFieldIdx = pField->GetFieldIndex();
bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
bool bMultiData = maDataFields.size() > 1;
if( !bDataLayout || bMultiData ) switch( eOrient )
{
case DataPilotFieldOrientation_ROW:
maRowFields.push_back( nFieldIdx );
if( bDataLayout )
maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
break;
case DataPilotFieldOrientation_COLUMN:
maColFields.push_back( nFieldIdx );
if( bDataLayout )
maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
break;
case DataPilotFieldOrientation_PAGE:
maPageFields.push_back( nFieldIdx );
DBG_ASSERT( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
break;
case DataPilotFieldOrientation_DATA:
DBG_ERRORFILE( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
break;
default:;
}
}
}
void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
{
if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
{
// field properties
pField->SetDataPropertiesFromDim( rSaveDim );
// update the data field position list
maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) );
}
}
void XclExpPivotTable::Finalize()
{
// field numbers
maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
// subtotal items
for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
maFieldList.GetRecord( nPos )->AppendSubtotalItems();
// find data field orientation field
maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
const ScfUInt16Vec* pFieldVec = 0;
switch( maPTInfo.mnDataAxis )
{
case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
}
if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
{
ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
if( aIt != pFieldVec->end() )
maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() );
}
// single data field is always row oriented
if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
// update output range (initialized in ctor)
sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
sal_uInt16& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
sal_uInt16& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
// exclude page fields from output range
rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
// exclude filter button from output range
if( mbFilterBtn )
++rnXclRow1;
// exclude empty row between (filter button and/or page fields) and table
if( mbFilterBtn || maPTInfo.mnPageFields )
++rnXclRow1;
// data area
sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
sal_uInt16& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
if( maDataFields.empty() )
++rnDataXclRow;
bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
if (bExtraHeaderRow)
// Insert an extra row only when there is no column field.
++rnDataXclRow;
rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
// first heading
maPTInfo.mnFirstHeadRow = rnXclRow1;
if (bExtraHeaderRow)
maPTInfo.mnFirstHeadRow += 2;
}
// records ----------------------------------------------------------------
void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.Len() + maPTInfo.maDataName.Len() );
rStrm << maPTInfo;
rStrm.EndRecord();
}
void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields ) const
{
if( !rFields.empty() )
{
rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt )
rStrm << *aIt;
rStrm.EndRecord();
}
}
void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
{
if( !maPageFields.empty() )
{
rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
rStrm.SetSliceSize( 6 );
for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
{
XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt );
if( xField.is() )
xField->WriteSxpiEntry( rStrm );
}
rStrm.EndRecord();
}
}
void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
{
for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
{
XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first );
if( xField.is() )
xField->WriteSxdi( rStrm, aIt->second );
}
}
void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const
{
if( nLineCount > 0 )
{
sal_uInt16 nLineSize = 8 + 2 * nIndexCount;
rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
/* #158444# Excel expects the records to be filled completely, do not
set a segment size... */
// rStrm.SetSliceSize( nLineSize );
for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
{
// #106598# Excel XP needs a partly initialized SXLI record
rStrm << sal_uInt16( 0 ) // number of equal index entries
<< EXC_SXVI_TYPE_DATA
<< nIndexCount
<< EXC_SXLI_DEFAULTFLAGS;
rStrm.WriteZeroBytes( 2 * nIndexCount );
}
rStrm.EndRecord();
}
}
void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
{
rStrm.StartRecord( EXC_ID_SXEX, 24 );
rStrm << maPTExtInfo;
rStrm.EndRecord();
}
void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
{
rStrm.StartRecord( 0x0802, 32 );
sal_uInt16 nRecordType = 0x0802;
sal_uInt16 nDummyFlags = 0x0000;
sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table
rStrm << nRecordType << nDummyFlags << nTableType;
// General flags
bool bEnableRefresh = true;
bool bPCacheInvalid = false;
bool bOlapPTReport = false;
sal_uInt16 nFlags = 0x0000;
if (bEnableRefresh) nFlags |= 0x0001;
if (bPCacheInvalid) nFlags |= 0x0002;
if (bOlapPTReport) nFlags |= 0x0004;
rStrm << nFlags;
// Feature-specific options. The value differs depending on the table
// type, but we assume the table type is always pivot table.
sal_uInt32 nOptions = 0x00000000;
bool bNoStencil = false;
bool bHideTotal = false;
bool bEmptyRows = false;
bool bEmptyCols = false;
if (bNoStencil) nOptions |= 0x00000001;
if (bHideTotal) nOptions |= 0x00000002;
if (bEmptyRows) nOptions |= 0x00000008;
if (bEmptyCols) nOptions |= 0x00000010;
rStrm << nOptions;
enum ExcelVersion
{
Excel2000 = 0,
ExcelXP = 1,
Excel2003 = 2,
Excel2007 = 3
};
ExcelVersion eXclVer = Excel2000;
sal_uInt8 nOffsetBytes = 16;
rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed
<< static_cast<sal_uInt8>(eXclVer) // minimum version to refresh
<< nOffsetBytes
<< static_cast<sal_uInt8>(eXclVer); // first version created
rStrm << XclExpString(maPTInfo.maTableName);
rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
rStrm.EndRecord();
}
void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
{
// Until we sync the autoformat ids only export if using grid header layout
// That could only have been set via xls import so far.
if ( 0 == maPTViewEx9Info.mnGridLayout )
{
rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
rStrm << maPTViewEx9Info;
rStrm.EndRecord();
}
}
// ============================================================================
namespace {
const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
/** Record wrapper class to write the pivot caches or pivot tables. */
class XclExpPivotRecWrapper : public XclExpRecordBase
{
public:
explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
virtual void Save( XclExpStream& rStrm );
virtual void SaveXml( XclExpXmlStream& rStrm );
private:
XclExpPivotTableManager& mrPTMgr;
SCTAB mnScTab;
};
XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
mrPTMgr( rPTMgr ),
mnScTab( nScTab )
{
}
void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
{
if( mnScTab == EXC_PTMGR_PIVOTCACHES )
mrPTMgr.WritePivotCaches( rStrm );
else
mrPTMgr.WritePivotTables( rStrm, mnScTab );
}
void XclExpPivotRecWrapper::SaveXml( XclExpXmlStream& rStrm )
{
if( mnScTab == EXC_PTMGR_PIVOTCACHES )
mrPTMgr.WritePivotCachesXml( rStrm );
else
mrPTMgr.WritePivotTablesXml( rStrm, mnScTab );
}
} // namespace
// ----------------------------------------------------------------------------
XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
XclExpRoot( rRoot ),
mbShareCaches( true )
{
}
void XclExpPivotTableManager::CreatePivotTables()
{
if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
for( sal_uInt16 nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] )
if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) )
maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache ) );
}
XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
{
return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
}
XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
{
return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
}
void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
{
maPCacheList.Save( rStrm );
}
void XclExpPivotTableManager::WritePivotCachesXml( XclExpXmlStream& rStrm )
{
if( maPCacheList.IsEmpty() )
return;
sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
rWorkbook->startElement( XML_pivotCaches, FSEND );
maPCacheList.SaveXml( rStrm );
rWorkbook->endElement( XML_pivotCaches );
}
void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
{
for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
{
XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
if( xPTable->GetScTab() == nScTab )
xPTable->Save( rStrm );
}
}
void XclExpPivotTableManager::WritePivotTablesXml( XclExpXmlStream& rStrm, SCTAB nScTab )
{
for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
{
XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
if( xPTable->GetScTab() == nScTab )
xPTable->SaveXml( rStrm );
}
}
// private --------------------------------------------------------------------
const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
{
// try to find a pivot cache with the same data source
/* #i25110# In Excel, the pivot cache contains additional fields
(i.e. grouping info, calculated fields). If the passed DataPilot object
or the found cache contains this data, do not share the cache with
multiple pivot tables. */
if( mbShareCaches )
{
if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
{
const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
// no dimension save data at all or save data does not contain grouping info
if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
{
// check all existing pivot caches
for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
{
XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
// pivot cache does not have grouping info and source data is equal
if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
return xPCache.get();
}
}
}
}
// create a new pivot cache
sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
if( xNewPCache->IsValid() )
{
maPCacheList.AppendRecord( xNewPCache );
return xNewPCache.get();
}
return 0;
}
// ============================================================================