blob: 4844c078ca9fb17f0d9eab38ed492444304ec6e8 [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 "xipivot.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 <tools/datetime.hxx>
#include <svl/zformat.hxx>
#include <svl/intitem.hxx>
#include "document.hxx"
#include "cell.hxx"
#include "dpsave.hxx"
#include "dpdimsave.hxx"
#include "dpobject.hxx"
#include "dpshttab.hxx"
#include "dpoutputgeometry.hxx"
#include "scitems.hxx"
#include "attrib.hxx"
#include "xltracer.hxx"
#include "xistream.hxx"
#include "xihelper.hxx"
#include "xilink.hxx"
#include "xiescher.hxx"
//! TODO ExcelToSc usage
#include "excform.hxx"
#include "xltable.hxx"
#include <vector>
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
using ::com::sun::star::sheet::DataPilotFieldOrientation;
using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
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 ::std::vector;
// ============================================================================
// Pivot cache
// ============================================================================
XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
{
switch( rStrm.GetRecId() )
{
case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
default: DBG_ERRORFILE( "XclImpPCItem::XclImpPCItem - unknown record id" );
}
}
namespace {
void lclSetValue( const XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, short nFormatType )
{
ScDocument& rDoc = rRoot.GetDoc();
rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), fValue );
sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
rDoc.ApplyAttr( rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ) );
}
} // namespace
void XclImpPCItem::WriteToSource( const XclImpRoot& rRoot, const ScAddress& rScPos ) const
{
ScDocument& rDoc = rRoot.GetDoc();
if( const String* pText = GetText() )
rDoc.SetString( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pText );
else if( const double* pfValue = GetDouble() )
rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pfValue );
else if( const sal_Int16* pnValue = GetInteger() )
rDoc.SetValue( rScPos.Col(), rScPos.Row(), rScPos.Tab(), *pnValue );
else if( const bool* pbValue = GetBool() )
lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, NUMBERFORMAT_LOGICAL );
else if( const DateTime* pDateTime = GetDateTime() )
{
// set number format date, time, or date/time, depending on the value
double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
double fInt = 0.0;
double fFrac = modf( fValue, &fInt );
short nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? NUMBERFORMAT_DATE :
((fInt == 0.0) ? NUMBERFORMAT_TIME : NUMBERFORMAT_DATETIME);
lclSetValue( rRoot, rScPos, fValue, nFormatType );
}
else if( const sal_uInt16* pnError = GetError() )
{
double fValue;
sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
const ScTokenArray* pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
XclTools::ErrorToEnum( fValue, EXC_BOOLERR_ERROR, nErrCode ) );
ScFormulaCell* pCell = new ScFormulaCell( &rDoc, rScPos, pScTokArr );
pCell->SetHybridDouble( fValue );
rDoc.PutCell( rScPos, pCell );
}
}
void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
SetDouble( rStrm.ReadDouble() );
}
void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
SetBool( rStrm.ReaduInt16() != 0 );
}
void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
SetError( rStrm.ReaduInt16() );
}
void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
SetInteger( rStrm.ReadInt16() );
}
void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
SetText( rStrm.ReadUniString() );
}
void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
{
DBG_ASSERT( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
sal_uInt16 nYear, nMonth;
sal_uInt8 nDay, nHour, nMin, nSec;
rStrm >> nYear >> nMonth >> nDay >> nHour >> nMin >> nSec;
SetDateTime( DateTime( Date( nDay, nMonth, nYear ), Time( nHour, nMin, nSec ) ) );
}
void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
{
(void)rStrm; // avoid compiler warning
DBG_ASSERT( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
SetEmpty();
}
// ============================================================================
XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
XclImpRoot( rRoot ),
mrPCache( rPCache ),
mnSourceScCol( -1 ),
mbNumGroupInfoRead( false )
{
}
XclImpPCField::~XclImpPCField()
{
}
// general field/item access --------------------------------------------------
const String& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
{
if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
{
const String& rVisName = rVisNames[ mnFieldIdx ];
if( rVisName.Len() > 0 )
return rVisName;
}
return maFieldInfo.maName;
}
const XclImpPCField* XclImpPCField::GetGroupBaseField() const
{
DBG_ASSERT( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : 0;
}
sal_uInt16 XclImpPCField::GetItemCount() const
{
return static_cast< sal_uInt16 >( maItems.size() );
}
const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
{
return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : 0;
}
const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
{
DBG_ASSERT( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
DBG_ASSERT( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : 0;
}
void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab ) const
{
DBG_ASSERT( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
GetDoc().SetString( nScCol, 0, nScTab, maFieldInfo.maName );
mnSourceScCol = nScCol;
}
void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx ) const
{
if( nItemIdx < maOrigItems.size() )
maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
}
void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab ) const
{
if( !maOrigItems.empty() )
maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
}
// records --------------------------------------------------------------------
void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
{
rStrm >> maFieldInfo;
/* Detect the type of this field. This is done very restrictive to detect
any unexpected state. */
meFieldType = EXC_PCFIELD_UNKNOWN;
bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
sal_uInt16 nVisC = maFieldInfo.mnVisItems;
sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
DBG_ASSERT( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
bool bType =
(nType == EXC_SXFIELD_DATA_STR) ||
(nType == EXC_SXFIELD_DATA_INT) ||
(nType == EXC_SXFIELD_DATA_DBL) ||
(nType == EXC_SXFIELD_DATA_STR_INT) ||
(nType == EXC_SXFIELD_DATA_STR_DBL) ||
(nType == EXC_SXFIELD_DATA_DATE) ||
(nType == EXC_SXFIELD_DATA_DATE_EMP) ||
(nType == EXC_SXFIELD_DATA_DATE_NUM) ||
(nType == EXC_SXFIELD_DATA_DATE_STR);
bool bTypeNone =
(nType == EXC_SXFIELD_DATA_NONE);
// for now, ignore data type of calculated fields
DBG_ASSERT( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
if( nVisC > 0 || bPostp )
{
if( bItems && !bPostp )
{
if( !bCalced )
{
// 1) standard fields and standard grouping fields
if( !bNum )
{
// 1a) standard field without grouping
if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
meFieldType = EXC_PCFIELD_STANDARD;
// 1b) standard grouping field
else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
meFieldType = EXC_PCFIELD_STDGROUP;
}
// 2) numerical grouping fields
else if( (nGroupC == nVisC) && (nBaseC == 0) )
{
// 2a) single num/date grouping field without child grouping field
if( !bChild && bType && (nOrigC > 0) )
{
switch( nType )
{
case EXC_SXFIELD_DATA_INT:
case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
default: DBG_ERRORFILE( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
}
}
// 2b) first date grouping field with child grouping field
else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
meFieldType = EXC_PCFIELD_DATEGROUP;
// 2c) additional date grouping field
else if( bTypeNone && (nOrigC == 0) )
meFieldType = EXC_PCFIELD_DATECHILD;
}
DBG_ASSERT( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
}
// 3) calculated field
else
{
if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
meFieldType = EXC_PCFIELD_CALCED;
DBG_ASSERT( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
}
}
else if( !bItems && bPostp )
{
// 4) standard field with postponed items
if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
meFieldType = EXC_PCFIELD_STANDARD;
DBG_ASSERT( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
}
}
}
void XclImpPCField::ReadItem( XclImpStream& rStrm )
{
DBG_ASSERT( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
// read the item
XclImpPCItemRef xItem( new XclImpPCItem( rStrm ) );
// try to insert into an item list
if( mbNumGroupInfoRead )
{
// there are 3 items after SXNUMGROUP that contain grouping limits and step count
if( maNumGroupItems.size() < 3 )
maNumGroupItems.push_back( xItem );
else
maOrigItems.push_back( xItem );
}
else if( HasInlineItems() || HasPostponedItems() )
{
maItems.push_back( xItem );
// visible item is original item in standard fields
if( IsStandardField() )
maOrigItems.push_back( xItem );
}
}
void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
{
DBG_ASSERT( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
DBG_ASSERT( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
DBG_ASSERT( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
rStrm >> maNumGroupInfo;
mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
}
void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
{
DBG_ASSERT( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
DBG_ASSERT( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
DBG_ASSERT( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
DBG_ASSERT( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
maGroupOrder.clear();
size_t nSize = rStrm.GetRecLeft() / 2;
maGroupOrder.resize( nSize, 0 );
for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
rStrm >> maGroupOrder[ nIdx ];
}
// grouping -------------------------------------------------------------------
void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
{
if( GetFieldName( rVisNames ).Len() > 0 )
{
if( IsStdGroupField() )
ConvertStdGroupField( rSaveData, rVisNames );
else if( IsNumGroupField() )
ConvertNumGroupField( rSaveData, rVisNames );
else if( IsDateGroupField() )
ConvertDateGroupField( rSaveData, rVisNames );
}
}
// private --------------------------------------------------------------------
void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
{
if( const XclImpPCField* pBaseField = GetGroupBaseField() )
{
const String& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
if( rBaseFieldName.Len() > 0 )
{
// *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
typedef ::std::vector< ScDPSaveGroupItem > ScDPSaveGroupItemVec;
ScDPSaveGroupItemVec aGroupItems;
aGroupItems.reserve( maItems.size() );
// initialize with own item names
for( XclImpPCItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt )
aGroupItems.push_back( ScDPSaveGroupItem( (*aIt)->ConvertToText() ) );
// *** iterate over all base items, set their names at corresponding own items ***
for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
if( *pBaseItem != *pGroupItem )
aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
// *** create the ScDPSaveGroupDimension object, fill with grouping info ***
ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
for( ScDPSaveGroupItemVec::const_iterator aIt = aGroupItems.begin(), aEnd = aGroupItems.end(); aIt != aEnd; ++aIt )
if( !aIt->IsEmpty() )
aGroupDim.AddGroupItem( *aIt );
rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
}
}
}
void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
{
ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
}
void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
{
ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
switch( meFieldType )
{
case EXC_PCFIELD_DATEGROUP:
{
if( aDateInfo.DateValues )
{
// special case for days only with step value - create numeric grouping
ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
}
else
{
ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
}
}
break;
case EXC_PCFIELD_DATECHILD:
{
if( const XclImpPCField* pBaseField = GetGroupBaseField() )
{
const String& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
if( rBaseFieldName.Len() > 0 )
{
ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
aGroupDim.SetDateInfo( aDateInfo, nScDateType );
rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
}
}
}
break;
default:
DBG_ERRORFILE( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
}
}
ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
{
ScDPNumGroupInfo aNumInfo;
aNumInfo.Enable = sal_True;
aNumInfo.DateValues = sal_False;
aNumInfo.AutoStart = sal_True;
aNumInfo.AutoEnd = sal_True;
if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
{
aNumInfo.Start = *pfMinValue;
aNumInfo.AutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
}
if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
{
aNumInfo.End = *pfMaxValue;
aNumInfo.AutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
}
if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
aNumInfo.Step = *pfStepValue;
return aNumInfo;
}
ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
{
ScDPNumGroupInfo aDateInfo;
aDateInfo.Enable = sal_True;
aDateInfo.DateValues = sal_False;
aDateInfo.AutoStart = sal_True;
aDateInfo.AutoEnd = sal_True;
if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
{
aDateInfo.Start = GetDoubleFromDateTime( *pMinDate );
aDateInfo.AutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
}
if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
{
aDateInfo.End = GetDoubleFromDateTime( *pMaxDate );
aDateInfo.AutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
}
// GetDateGroupStep() returns a value for date type "day" in single date groups only
if( const sal_Int16* pnStepValue = GetDateGroupStep() )
{
aDateInfo.Step = *pnStepValue;
aDateInfo.DateValues = sal_True;
}
return aDateInfo;
}
const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
{
DBG_ASSERT( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
{
DBG_ASSERT( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
return pItem->GetDouble();
}
return 0;
}
const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
{
DBG_ASSERT( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
{
DBG_ASSERT( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
return pItem->GetDateTime();
}
return 0;
}
const sal_Int16* XclImpPCField::GetDateGroupStep() const
{
// only for single date grouping fields, not for grouping chains
if( !IsGroupBaseField() && !IsGroupChildField() )
{
// only days may have a step value, return 0 for all other date types
if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
{
if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
{
DBG_ASSERT( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
if( const sal_Int16* pnStep = pItem->GetInteger() )
{
DBG_ASSERT( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
// return nothing for step count 1 - this is also a standard date group in Excel
return (*pnStep > 1) ? pnStep : 0;
}
}
}
}
return 0;
}
// ============================================================================
XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
XclImpRoot( rRoot ),
maSrcRange( ScAddress::INITIALIZE_INVALID ),
mnStrmId( 0 ),
mnSrcType( EXC_SXVS_UNKNOWN ),
mbSelfRef( false )
{
}
XclImpPivotCache::~XclImpPivotCache()
{
}
// data access ----------------------------------------------------------------
sal_uInt16 XclImpPivotCache::GetFieldCount() const
{
return static_cast< sal_uInt16 >( maFields.size() );
}
const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
{
return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0;
}
// records --------------------------------------------------------------------
void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
{
rStrm >> mnStrmId;
}
void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
{
rStrm >> mnSrcType;
GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
}
void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
{
/* Read DCONREF only once (by checking maTabName), there may be other
DCONREF records in another context. Read reference only if a leading
SXVS record is present (by checking mnSrcType). */
if( (maTabName.Len() > 0) || (mnSrcType != EXC_SXVS_SHEET) )
return;
XclRange aXclRange( ScAddress::UNINITIALIZED );
aXclRange.Read( rStrm, false );
String aEncUrl = rStrm.ReadUniString();
XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
/* Do not convert maTabName to Calc sheet name -> original name is used to
find the sheet in the document. Sheet index of source range will be
found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
may not exist yet. */
if( mbSelfRef )
GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
}
void XclImpPivotCache::ReadPivotCacheStream( XclImpStream& rStrm )
{
if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
return;
ScDocument& rDoc = GetDoc();
SCCOL nFieldScCol = 0; // column index of source data for next field
SCROW nItemScRow = 0; // row index of source data for current items
SCTAB nScTab = 0; // sheet index of source data
bool bGenerateSource = false; // true = write source data from cache to dummy table
if( mbSelfRef )
{
// try to find internal sheet containing the source data
nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
if( rDoc.HasTable( nScTab ) )
{
// set sheet index to source range
maSrcRange.aStart.SetTab( nScTab );
maSrcRange.aEnd.SetTab( nScTab );
}
else
{
// create dummy sheet for deleted internal sheet
bGenerateSource = true;
}
}
else
{
// create dummy sheet for external sheet
bGenerateSource = true;
}
// create dummy sheet for source data from external or deleted sheet
if( bGenerateSource )
{
if( rDoc.GetTableCount() >= MAXTABCOUNT )
// cannot create more sheets -> exit
return;
nScTab = rDoc.GetTableCount();
rDoc.MakeTable( nScTab );
String aDummyName = CREATE_STRING( "DPCache" );
if( maTabName.Len() > 0 )
aDummyName.Append( '_' ).Append( maTabName );
rDoc.CreateValidTabName( aDummyName );
rDoc.RenameTab( nScTab, aDummyName );
// set sheet index to source range
maSrcRange.aStart.SetTab( nScTab );
maSrcRange.aEnd.SetTab( nScTab );
}
// open pivot cache storage stream
SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
if( !xSvStrm.Is() )
return;
// create Excel record stream object
XclImpStream aPCStrm( *xSvStrm, GetRoot() );
aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
XclImpPCFieldRef xCurrField; // current field for new items
XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
size_t nPostpIdx = 0; // index to current field with postponed items
bool bLoop = true; // true = continue loop
while( bLoop && aPCStrm.StartNextRecord() )
{
switch( aPCStrm.GetRecId() )
{
case EXC_ID_EOF:
bLoop = false;
break;
case EXC_ID_SXDB:
aPCStrm >> maPCInfo;
break;
case EXC_ID_SXFIELD:
{
xCurrField.reset();
sal_uInt16 nNewFieldIdx = GetFieldCount();
if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
{
xCurrField.reset( new XclImpPCField( GetRoot(), *this, nNewFieldIdx ) );
maFields.push_back( xCurrField );
xCurrField->ReadSxfield( aPCStrm );
if( xCurrField->HasOrigItems() )
{
if( xCurrField->HasPostponedItems() )
aPostpFields.push_back( xCurrField );
else
aOrigFields.push_back( xCurrField );
// insert field name into generated source data, field remembers its column index
if( bGenerateSource && (nFieldScCol <= MAXCOL) )
xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
}
// do not read items into invalid/postponed fields
if( !xCurrField->HasInlineItems() )
xCurrField.reset();
}
}
break;
case EXC_ID_SXINDEXLIST:
// read index list and insert all items into generated source data
if( bGenerateSource && (nItemScRow <= MAXROW) && (++nItemScRow <= MAXROW) )
{
for( XclImpPCFieldVec::const_iterator aIt = aOrigFields.begin(), aEnd = aOrigFields.end(); aIt != aEnd; ++aIt )
{
sal_uInt16 nItemIdx = (*aIt)->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
(*aIt)->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
}
}
xCurrField.reset();
break;
case EXC_ID_SXDOUBLE:
case EXC_ID_SXBOOLEAN:
case EXC_ID_SXERROR:
case EXC_ID_SXINTEGER:
case EXC_ID_SXSTRING:
case EXC_ID_SXDATETIME:
case EXC_ID_SXEMPTY:
if( xCurrField.is() ) // inline items
{
xCurrField->ReadItem( aPCStrm );
}
else if( !aPostpFields.empty() ) // postponed items
{
// read postponed item
aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
// write item to source
if( bGenerateSource && (nItemScRow <= MAXROW) )
{
// start new row, if there are only postponed fields
if( aOrigFields.empty() && (nPostpIdx == 0) )
++nItemScRow;
if( nItemScRow <= MAXROW )
aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
}
// get index of next postponed field
++nPostpIdx;
if( nPostpIdx >= aPostpFields.size() )
nPostpIdx = 0;
}
break;
case EXC_ID_SXNUMGROUP:
if( xCurrField.is() )
xCurrField->ReadSxnumgroup( aPCStrm );
break;
case EXC_ID_SXGROUPINFO:
if( xCurrField.is() )
xCurrField->ReadSxgroupinfo( aPCStrm );
break;
// known but ignored records
case EXC_ID_SXRULE:
case EXC_ID_SXFILT:
case EXC_ID_00F5:
case EXC_ID_SXNAME:
case EXC_ID_SXPAIR:
case EXC_ID_SXFMLA:
case EXC_ID_SXFORMULA:
case EXC_ID_SXDBEX:
case EXC_ID_SXFDBTYPE:
break;
default:
DBG_ERROR1( "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x%04hX", aPCStrm.GetRecId() );
}
}
DBG_ASSERT( maPCInfo.mnTotalFields == maFields.size(),
"XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
// set source range for external source data
if( bGenerateSource && (nFieldScCol > 0) )
{
maSrcRange.aStart.SetCol( 0 );
maSrcRange.aStart.SetRow( 0 );
// nFieldScCol points to first unused column
maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
// nItemScRow points to last used row
maSrcRange.aEnd.SetRow( nItemScRow );
}
}
bool XclImpPivotCache::IsRefreshOnLoad() const
{
return static_cast<bool>(maPCInfo.mnFlags & 0x0004);
}
// ============================================================================
// Pivot table
// ============================================================================
XclImpPTItem::XclImpPTItem( const XclImpPTField& rPTField ) :
mrPTField( rPTField )
{
}
const String* XclImpPTItem::GetItemName() const
{
if( const XclImpPCField * mpCacheField = mrPTField.GetCacheField() )
{
if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
{
return pCacheItem->GetItemName();
}
}
return 0;
}
const String* XclImpPTItem::GetVisItemName() const
{
return maItemInfo.HasVisName() ? maItemInfo.GetVisName() : GetItemName();
}
void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
{
rStrm >> maItemInfo;
}
void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim ) const
{
if( const String* pItemName = GetItemName() )
{
ScDPSaveMember& rMember = *rSaveDim.GetMemberByName( *pItemName );
rMember.SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
rMember.SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
if (maItemInfo.HasVisName())
rMember.SetLayoutName(*maItemInfo.GetVisName());
}
}
// ============================================================================
XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
mrPTable( rPTable )
{
maFieldInfo.mnCacheIdx = nCacheIdx;
}
// general field/item access --------------------------------------------------
const XclImpPCField* XclImpPTField::GetCacheField() const
{
XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
return xPCache.is() ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : 0;
}
const String& XclImpPTField::GetFieldName() const
{
const XclImpPCField* pField = GetCacheField();
return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : String::EmptyString();
}
const String& XclImpPTField::GetVisFieldName() const
{
const String* pVisName = maFieldInfo.GetVisName();
return pVisName ? *pVisName : String::EmptyString();
}
const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
{
return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : 0;
}
const String* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
{
const XclImpPTItem* pItem = GetItem( nItemIdx );
return pItem ? pItem->GetItemName() : 0;
}
const String* XclImpPTField::GetVisItemName( sal_uInt16 nItemIdx ) const
{
const XclImpPTItem* pItem = GetItem( nItemIdx );
return pItem ? pItem->GetVisItemName() : 0;
}
// records --------------------------------------------------------------------
void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
{
rStrm >> maFieldInfo;
}
void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
{
rStrm >> maFieldExtInfo;
}
void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
{
XclImpPTItemRef xItem( new XclImpPTItem( *this ) );
maItems.push_back( xItem );
xItem->ReadSxvi( rStrm );
}
// row/column fields ----------------------------------------------------------
void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
{
DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
// special data orientation field?
if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
rSaveData.GetDataLayoutDimension()->SetOrientation( static_cast< sal_uInt16 >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) ) );
else
ConvertRCPField( rSaveData );
}
// page fields ----------------------------------------------------------------
void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
{
maPageInfo = rPageInfo;
}
void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
{
DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
if( ScDPSaveDimension* pSaveDim = ConvertRCPField( rSaveData ) )
pSaveDim->SetCurrentPage( GetItemName( maPageInfo.mnSelItem ) );
}
// hidden fields --------------------------------------------------------------
void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
{
DBG_ASSERT( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
ConvertRCPField( rSaveData );
}
// data fields ----------------------------------------------------------------
bool XclImpPTField::HasDataFieldInfo() const
{
return !maDataInfoList.empty();
}
void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
{
DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
maDataInfoList.push_back( rDataInfo );
}
void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
{
DBG_ASSERT( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
DBG_ASSERT( !maDataInfoList.empty(), "XclImpPTField::ConvertDataField - no data field info" );
if( !maDataInfoList.empty() )
{
const String& rFieldName = GetFieldName();
if( rFieldName.Len() > 0 )
{
XclPTDataFieldInfoList::const_iterator aIt = maDataInfoList.begin(), aEnd = maDataInfoList.end();
ScDPSaveDimension& rSaveDim = *rSaveData.GetNewDimensionByName( rFieldName );
ConvertDataField( rSaveDim, *aIt );
// multiple data fields -> clone dimension
for( ++aIt; aIt != aEnd; ++aIt )
{
ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( rSaveDim );
ConvertDataFieldInfo( rDupDim, *aIt );
}
}
}
}
// private --------------------------------------------------------------------
/**
* Convert Excel-encoded subtotal name to a Calc-encoded one.
*/
static OUString lcl_convertExcelSubtotalName(const OUString& rName)
{
OUStringBuffer aBuf;
const sal_Unicode* p = rName.getStr();
sal_Int32 n = rName.getLength();
for (sal_Int32 i = 0; i < n; ++i)
{
const sal_Unicode c = p[i];
if (c == sal_Unicode('\\'))
{
aBuf.append(c);
aBuf.append(c);
}
else
aBuf.append(c);
}
return aBuf.makeStringAndClear();
}
ScDPSaveDimension* XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
{
const String& rFieldName = GetFieldName();
if( rFieldName.Len() == 0 )
return 0;
const XclImpPCField* pCacheField = GetCacheField();
if( !pCacheField || !pCacheField->IsSupportedField() )
return 0;
ScDPSaveDimension& rSaveDim = *rSaveData.GetNewDimensionByName( rFieldName );
// orientation
rSaveDim.SetOrientation( static_cast< sal_uInt16 >( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) ) );
// general field info
ConvertFieldInfo( rSaveDim );
// visible name
if( const String* pVisName = maFieldInfo.GetVisName() )
if( pVisName->Len() > 0 )
rSaveDim.SetLayoutName( *pVisName );
// subtotal function(s)
XclPTSubtotalVec aSubtotalVec;
maFieldInfo.GetSubtotals( aSubtotalVec );
if( !aSubtotalVec.empty() )
rSaveDim.SetSubTotals( static_cast< long >( aSubtotalVec.size() ), &aSubtotalVec[ 0 ] );
// sorting
DataPilotFieldSortInfo aSortInfo;
aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
rSaveDim.SetSortInfo( &aSortInfo );
// auto show
DataPilotFieldAutoShowInfo aShowInfo;
aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
rSaveDim.SetAutoShowInfo( &aShowInfo );
// layout
DataPilotFieldLayoutInfo aLayoutInfo;
aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
rSaveDim.SetLayoutInfo( &aLayoutInfo );
// grouping info
pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
// custom subtotal name
if (maFieldExtInfo.mpFieldTotalName.get())
{
OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
rSaveDim.SetSubtotalName(aSubName);
}
return &rSaveDim;
}
void XclImpPTField::ConvertFieldInfo( ScDPSaveDimension& rSaveDim ) const
{
rSaveDim.SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
ConvertItems( rSaveDim );
}
void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
{
// orientation
rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
// general field info
ConvertFieldInfo( rSaveDim );
// extended data field info
ConvertDataFieldInfo( rSaveDim, rDataInfo );
}
void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
{
// visible name
if( const String* pVisName = rDataInfo.GetVisName() )
if( pVisName->Len() > 0 )
rSaveDim.SetLayoutName( *pVisName );
// aggregation function
rSaveDim.SetFunction( static_cast< sal_uInt16 >( rDataInfo.GetApiAggFunc() ) );
// result field reference
sal_Int32 nRefType = rDataInfo.GetApiRefType();
if( nRefType != ::com::sun::star::sheet::DataPilotFieldReferenceType::NONE )
{
DataPilotFieldReference aFieldRef;
aFieldRef.ReferenceType = nRefType;
if( const XclImpPTField* pRefField = mrPTable.GetField( rDataInfo.mnRefField ) )
{
aFieldRef.ReferenceField = pRefField->GetFieldName();
aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
if( aFieldRef.ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
if( const String* pRefItemName = pRefField->GetItemName( rDataInfo.mnRefItem ) )
aFieldRef.ReferenceItemName = *pRefItemName;
}
rSaveDim.SetReferenceValue( &aFieldRef );
}
}
void XclImpPTField::ConvertItems( ScDPSaveDimension& rSaveDim ) const
{
for( XclImpPTItemVec::const_iterator aIt = maItems.begin(), aEnd = maItems.end(); aIt != aEnd; ++aIt )
(*aIt)->ConvertItem( rSaveDim );
}
// ============================================================================
XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
XclImpRoot( rRoot ),
maDataOrientField( *this, EXC_SXIVD_DATA ),
mpDPObj(NULL)
{
}
XclImpPivotTable::~XclImpPivotTable()
{
}
// cache/field access, misc. --------------------------------------------------
sal_uInt16 XclImpPivotTable::GetFieldCount() const
{
return static_cast< sal_uInt16 >( maFields.size() );
}
const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
{
return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0);
}
XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
{
// do not return maDataOrientField
return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : 0;
}
const String& XclImpPivotTable::GetFieldName( sal_uInt16 nFieldIdx ) const
{
if( const XclImpPTField* pField = GetField( nFieldIdx ) )
return pField->GetFieldName();
return EMPTY_STRING;
}
const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
{
if( nDataFieldIdx < maOrigDataFields.size() )
return GetField( maOrigDataFields[ nDataFieldIdx ] );
return 0;
}
const String& XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
{
if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
return pField->GetFieldName();
return EMPTY_STRING;
}
// records --------------------------------------------------------------------
void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
{
rStrm >> maPTInfo;
GetAddressConverter().ConvertRange(
maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
mxCurrField.reset();
}
void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
{
sal_uInt16 nFieldCount = GetFieldCount();
if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
{
// cache index for the field is equal to the SXVD record index
mxCurrField.reset( new XclImpPTField( *this, nFieldCount ) );
maFields.push_back( mxCurrField );
mxCurrField->ReadSxvd( rStrm );
// add visible name of new field to list of visible names
maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
DBG_ASSERT( maFields.size() == maVisFieldNames.size(),
"XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
}
else
mxCurrField.reset();
}
void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
{
if( mxCurrField.is() )
mxCurrField->ReadSxvi( rStrm );
}
void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
{
if( mxCurrField.is() )
mxCurrField->ReadSxvdex( rStrm );
}
void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
{
mxCurrField.reset();
// find the index vector to fill (row SXIVD doesn't exist without row fields)
ScfUInt16Vec* pFieldVec = 0;
if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
pFieldVec = &maRowFields;
else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
pFieldVec = &maColFields;
// fill the vector from record data
if( pFieldVec )
{
sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
pFieldVec->reserve( nSize );
for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
{
sal_uInt16 nFieldIdx;
rStrm >> nFieldIdx;
pFieldVec->push_back( nFieldIdx );
// set orientation at special data orientation field
if( nFieldIdx == EXC_SXIVD_DATA )
{
sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
maDataOrientField.SetAxes( nAxis );
}
}
}
}
void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
{
mxCurrField.reset();
sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
{
XclPTPageFieldInfo aPageInfo;
rStrm >> aPageInfo;
if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
{
maPageFields.push_back( aPageInfo.mnField );
pField->SetPageFieldInfo( aPageInfo );
}
GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
}
}
void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
{
mxCurrField.reset();
XclPTDataFieldInfo aDataInfo;
rStrm >> aDataInfo;
if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
{
maOrigDataFields.push_back( aDataInfo.mnField );
// DataPilot does not support double data fields -> add first appearence to index list only
if( !pField->HasDataFieldInfo() )
maFiltDataFields.push_back( aDataInfo.mnField );
pField->AddDataFieldInfo( aDataInfo );
}
}
void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
{
rStrm >> maPTExtInfo;
}
void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
{
rStrm >> maPTViewEx9Info;
}
// ----------------------------------------------------------------------------
void XclImpPivotTable::Convert()
{
if( !mxPCache || !mxPCache->GetSourceRange().IsValid() )
return;
ScDPSaveData aSaveData;
// *** global settings ***
aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
aSaveData.SetFilterButton( sal_False );
aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
// *** fields ***
ScfUInt16Vec::const_iterator aIt, aEnd;
// row fields
for( aIt = maRowFields.begin(), aEnd = maRowFields.end(); aIt != aEnd; ++aIt )
if( const XclImpPTField* pField = GetField( *aIt ) )
pField->ConvertRowColField( aSaveData );
// column fields
for( aIt = maColFields.begin(), aEnd = maColFields.end(); aIt != aEnd; ++aIt )
if( const XclImpPTField* pField = GetField( *aIt ) )
pField->ConvertRowColField( aSaveData );
// page fields
for( aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
if( const XclImpPTField* pField = GetField( *aIt ) )
pField->ConvertPageField( aSaveData );
// We need to import hidden fields because hidden fields may contain
// special settings for subtotals (aggregation function, filters, custom
// name etc.) and members (hidden, custom name etc.).
// hidden fields
for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
if( const XclImpPTField* pField = GetField( nField ) )
if( (pField->GetAxes() & EXC_SXVD_AXIS_ROWCOLPAGE) == 0 )
pField->ConvertHiddenField( aSaveData );
// data fields
for( aIt = maFiltDataFields.begin(), aEnd = maFiltDataFields.end(); aIt != aEnd; ++aIt )
if( const XclImpPTField* pField = GetField( *aIt ) )
pField->ConvertDataField( aSaveData );
// *** insert into Calc document ***
// create source descriptor
ScSheetSourceDesc aDesc;
aDesc.aSourceRange = mxPCache->GetSourceRange();
// adjust output range to include the page fields
ScRange aOutRange( maOutScRange );
if( !maPageFields.empty() )
{
SCsROW nDecRows = ::std::min< SCsROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
aOutRange.aStart.IncRow( -nDecRows );
}
// create the DataPilot
ScDPObject* pDPObj = new ScDPObject( GetDocPtr() );
pDPObj->SetName( maPTInfo.maTableName );
if (maPTInfo.maDataName.Len() > 0)
aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
if (maPTViewEx9Info.maGrandTotalName.Len() > 0)
aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
pDPObj->SetSaveData( aSaveData );
pDPObj->SetSheetDesc( aDesc );
pDPObj->SetOutRange( aOutRange );
pDPObj->SetAlive( sal_True );
pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
GetDoc().GetDPCollection()->InsertNewTable(pDPObj);
mpDPObj = pDPObj;
ApplyMergeFlags(aOutRange, aSaveData);
MaybeRefresh(); // refresh after convert immediately
mxPCache = XclImpPivotCacheRef(); // release memory
}
void XclImpPivotTable::MaybeRefresh()
{
if (mpDPObj && mxPCache->IsRefreshOnLoad())
{
// 'refresh table on load' flag is set. Refresh the table now. Some
// Excel files contain partial table output when this flag is set.
ScRange aOutRange = mpDPObj->GetOutRange();
mpDPObj->Output(aOutRange.aStart);
}
}
void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
{
// Apply merge flags for varoius datapilot controls.
ScDPOutputGeometry aGeometry(rOutRange, false, ScDPOutputGeometry::XLS);
aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
// Excel includes data layout field in the row field count. We need to
// subtract it.
bool bDataLayout = maPTInfo.mnDataFields > 1;
aGeometry.setRowFieldCount(maPTInfo.mnRowFields - static_cast<sal_uInt32>(bDataLayout));
ScDocument& rDoc = GetDoc();
vector<ScAddress> aPageBtns;
aGeometry.getPageFieldPositions(aPageBtns);
vector<ScAddress>::const_iterator itr = aPageBtns.begin(), itrEnd = aPageBtns.end();
for (; itr != itrEnd; ++itr)
{
sal_uInt16 nMFlag = SC_MF_BUTTON;
String aName;
rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName);
if (rSaveData.HasInvisibleMember(aName))
nMFlag |= SC_MF_HIDDEN_MEMBER;
rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag);
rDoc.ApplyFlagsTab(itr->Col()+1, itr->Row(), itr->Col()+1, itr->Row(), itr->Tab(), SC_MF_AUTO);
}
vector<ScAddress> aColBtns;
aGeometry.getColumnFieldPositions(aColBtns);
itr = aColBtns.begin();
itrEnd = aColBtns.end();
for (; itr != itrEnd; ++itr)
{
sal_Int16 nMFlag = SC_MF_BUTTON | SC_MF_BUTTON_POPUP;
String aName;
rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName);
if (rSaveData.HasInvisibleMember(aName))
nMFlag |= SC_MF_HIDDEN_MEMBER;
rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag);
}
vector<ScAddress> aRowBtns;
aGeometry.getRowFieldPositions(aRowBtns);
if (aRowBtns.empty())
{
if (bDataLayout)
{
// No row fields, but the data layout button exists.
SCROW nRow = aGeometry.getRowFieldHeaderRow();
SCCOL nCol = rOutRange.aStart.Col();
SCTAB nTab = rOutRange.aStart.Tab();
rDoc.ApplyFlagsTab(nCol, nRow, nCol, nRow, nTab, SC_MF_BUTTON);
}
}
else
{
itr = aRowBtns.begin();
itrEnd = aRowBtns.end();
for (; itr != itrEnd; ++itr)
{
sal_Int16 nMFlag = SC_MF_BUTTON | SC_MF_BUTTON_POPUP;
String aName;
rDoc.GetString(itr->Col(), itr->Row(), itr->Tab(), aName);
if (rSaveData.HasInvisibleMember(aName))
nMFlag |= SC_MF_HIDDEN_MEMBER;
rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), nMFlag);
}
if (bDataLayout)
{
--itr; // move back to the last row field position.
rDoc.ApplyFlagsTab(itr->Col(), itr->Row(), itr->Col(), itr->Row(), itr->Tab(), SC_MF_BUTTON);
}
}
}
// ============================================================================
// ============================================================================
XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
XclImpRoot( rRoot )
{
}
XclImpPivotTableManager::~XclImpPivotTableManager()
{
}
// pivot cache records --------------------------------------------------------
XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
{
XclImpPivotCacheRef xPCache;
if( nCacheIdx < maPCaches.size() )
xPCache = maPCaches[ nCacheIdx ];
return xPCache;
}
void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
{
XclImpPivotCacheRef xPCache( new XclImpPivotCache( GetRoot() ) );
maPCaches.push_back( xPCache );
xPCache->ReadSxidstm( rStrm );
}
void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
{
if( !maPCaches.empty() )
maPCaches.back()->ReadSxvs( rStrm );
}
void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
{
if( !maPCaches.empty() )
maPCaches.back()->ReadDconref( rStrm );
}
// pivot table records --------------------------------------------------------
void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
{
XclImpPivotTableRef xPTable( new XclImpPivotTable( GetRoot() ) );
maPTables.push_back( xPTable );
xPTable->ReadSxview( rStrm );
}
void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxvd( rStrm );
}
void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxvdex( rStrm );
}
void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxivd( rStrm );
}
void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxpi( rStrm );
}
void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxdi( rStrm );
}
void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxvi( rStrm );
}
void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxex( rStrm );
}
void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
{
if( !maPTables.empty() )
maPTables.back()->ReadSxViewEx9( rStrm );
}
// ----------------------------------------------------------------------------
// Reading all used pivot caches at one time and then converting all pivot tables together will consume too much memory, forbid this action
// ConvertPivotTables will change to read cache one by one and convert it then release the memory
/*void XclImpPivotTableManager::ReadPivotCaches( XclImpStream& rStrm )
{
for( XclImpPivotCacheVec::iterator aIt = maPCaches.begin(), aEnd = maPCaches.end(); aIt != aEnd; ++aIt )
(*aIt)->ReadPivotCacheStream( rStrm );
}*/
void XclImpPivotTableManager::ConvertPivotTables( XclImpStream & rStm/* guoyanp: for DP memory */ )
{
// for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
// (*aIt)->Convert();
std::map< sal_uInt16, std::list< XclImpPivotTableRef > > aMap;
for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
aMap[(*aIt)->GetCacheId()].push_back( *aIt );
size_t iCache = 0;
for( std::map< sal_uInt16, std::list< XclImpPivotTableRef > >::iterator i = aMap.begin(); i != aMap.end(); i++, iCache++ )
{
if( i->first >= maPCaches.size() ) continue;
maPCaches[iCache]->ReadPivotCacheStream( rStm );
for( std::list< XclImpPivotTableRef >::iterator j = i->second.begin(); j != i->second.end(); j++ )
(*j)->Convert();
maPCaches[iCache] = XclImpPivotCacheRef();
}
}
// Reading all used pivot caches at one time and then converting all pivot tables together will consume too much memory, forbid that action
// ConvertPivotTables will change to read cache one by one and convert it then release the memory
// So refreshing all pivot tables at one time is forbidden too because the cache already released
// Need to refresh one by one after convert every pivot table
/*void XclImpPivotTableManager::MaybeRefreshPivotTables()
{
for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
(*aIt)->MaybeRefresh();
}*/
// ============================================================================