blob: 2b0b2baa526131d0958c1bfe59f14805f26be02e [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 ---------------------------------------------------------------
#include "dpobject.hxx"
#include "dptabsrc.hxx"
#include "dpsave.hxx"
#include "dpdimsave.hxx"
#include "dpoutput.hxx"
#include "dpshttab.hxx"
#include "dpsdbtab.hxx"
#include "dpgroup.hxx"
#include "document.hxx"
#include "rechead.hxx"
#include "pivot.hxx" // PIVOT_DATA_FIELD
#include "dapiuno.hxx" // ScDataPilotConversion
#include "miscuno.hxx"
#include "scerrors.hxx"
#include "refupdat.hxx"
#include "scresid.hxx"
#include "sc.hrc"
#include "attrib.hxx"
#include "scitems.hxx"
#include "unonames.hxx"
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
#include "dpglobal.hxx"
#include "globstr.hrc"
// End Comments
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/sheet/GeneralFunction.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
#include <com/sun/star/sheet/DataPilotTablePositionData.hpp>
#include <com/sun/star/sheet/DataPilotTablePositionType.hpp>
#include <com/sun/star/sheet/DimensionFlags.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
#include <comphelper/processfactory.hxx>
#include <tools/debug.hxx>
#include <tools/diagnose_ex.h>
#include <svl/zforlist.hxx> // IsNumberFormat
#include <vector>
#include <stdio.h>
using namespace com::sun::star;
using ::std::vector;
using ::boost::shared_ptr;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::lang::XComponent;
using ::com::sun::star::sheet::DataPilotTableHeaderData;
using ::com::sun::star::sheet::DataPilotTablePositionData;
using ::com::sun::star::beans::XPropertySet;
using ::rtl::OUString;
// -----------------------------------------------------------------------
#define SCDPSOURCE_SERVICE "com.sun.star.sheet.DataPilotSource"
// -----------------------------------------------------------------------
// incompatible versions of data pilot files
#define SC_DP_VERSION_CURRENT 6
// type of source data
#define SC_DP_SOURCE_SHEET 0
#define SC_DP_SOURCE_DATABASE 1
#define SC_DP_SOURCE_SERVICE 2
// -----------------------------------------------------------------------
//! move to a header file
#define DP_PROP_COLUMNGRAND "ColumnGrand"
#define DP_PROP_FUNCTION "Function"
#define DP_PROP_IGNOREEMPTY "IgnoreEmptyRows"
#define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension"
//#define DP_PROP_ISVISIBLE "IsVisible"
#define DP_PROP_ORIENTATION "Orientation"
#define DP_PROP_ORIGINAL "Original"
#define DP_PROP_POSITION "Position"
#define DP_PROP_REPEATIFEMPTY "RepeatIfEmpty"
#define DP_PROP_ROWGRAND "RowGrand"
#define DP_PROP_SHOWDETAILS "ShowDetails"
#define DP_PROP_SHOWEMPTY "ShowEmpty"
#define DP_PROP_SUBTOTALS "SubTotals"
#define DP_PROP_USEDHIERARCHY "UsedHierarchy"
// -----------------------------------------------------------------------
sal_uInt16 lcl_GetDataGetOrientation( const uno::Reference<sheet::XDimensionsSupplier>& xSource )
{
long nRet = sheet::DataPilotFieldOrientation_HIDDEN;
if ( xSource.is() )
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
long nIntCount = xIntDims->getCount();
sal_Bool bFound = sal_False;
for (long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++)
{
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nIntDim) );
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
if ( xDimProp.is() )
{
bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
//! error checking -- is "IsDataLayoutDimension" property required??
if (bFound)
nRet = ScUnoHelpFunctions::GetEnumProperty(
xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
sheet::DataPilotFieldOrientation_HIDDEN );
}
}
}
return static_cast< sal_uInt16 >( nRet );
}
// -----------------------------------------------------------------------
ScDPObject::ScDPObject( ScDocument* pD ) :
pDoc( pD ),
pSaveData( NULL ),
pSheetDesc( NULL ),
pImpDesc( NULL ),
pServDesc( NULL ),
mpTableData(static_cast<ScDPTableData*>(NULL)),
pOutput( NULL ),
bSettingsChanged( sal_False ),
bAlive( sal_False ),
bAllowMove( sal_False ),
nHeaderRows( 0 ),
mbHeaderLayout(false),
bRefresh( sal_False ), // Wang Xu Ming - DataPilot migration
mnCacheId( -1 ), // Wang Xu Ming - DataPilot migration
mbCreatingTableData( false )
{
}
ScDPObject::ScDPObject(const ScDPObject& r) :
ScDataObject(),
pDoc( r.pDoc ),
pSaveData( NULL ),
aTableName( r.aTableName ),
aTableTag( r.aTableTag ),
aOutRange( r.aOutRange ),
pSheetDesc( NULL ),
pImpDesc( NULL ),
pServDesc( NULL ),
mpTableData(static_cast<ScDPTableData*>(NULL)),
pOutput( NULL ),
bSettingsChanged( sal_False ),
bAlive( sal_False ),
bAllowMove( sal_False ),
nHeaderRows( r.nHeaderRows ),
mbHeaderLayout( r.mbHeaderLayout ),
bRefresh( r.bRefresh ), // Wang Xu Ming - DataPilot migration
mnCacheId ( r.mnCacheId ), // Wang Xu Ming - DataPilot migration
mbCreatingTableData( false )
{
if (r.pSaveData)
pSaveData = new ScDPSaveData(*r.pSaveData);
if (r.pSheetDesc)
pSheetDesc = new ScSheetSourceDesc(*r.pSheetDesc);
if (r.pImpDesc)
pImpDesc = new ScImportSourceDesc(*r.pImpDesc);
if (r.pServDesc)
pServDesc = new ScDPServiceDesc(*r.pServDesc);
// xSource (and pOutput) is not copied
}
ScDPObject::~ScDPObject()
{
delete pOutput;
delete pSaveData;
delete pSheetDesc;
delete pImpDesc;
delete pServDesc;
mnCacheId = -1; // Wang Xu Ming - DataPilot migration
InvalidateSource();
}
ScDataObject* ScDPObject::Clone() const
{
return new ScDPObject(*this);
}
void ScDPObject::SetAlive(sal_Bool bSet)
{
bAlive = bSet;
}
void ScDPObject::SetAllowMove(sal_Bool bSet)
{
bAllowMove = bSet;
}
void ScDPObject::SetSaveData(const ScDPSaveData& rData)
{
if ( pSaveData != &rData ) // API implementation modifies the original SaveData object
{
delete pSaveData;
pSaveData = new ScDPSaveData( rData );
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
if ( rData.GetCacheId() >= 0 )
mnCacheId = rData.GetCacheId();
else if ( mnCacheId >= 0 )
pSaveData->SetCacheId( mnCacheId );
// End Comments
}
InvalidateData(); // re-init source from SaveData
}
void ScDPObject::SetHeaderLayout (bool bUseGrid)
{
mbHeaderLayout = bUseGrid;
}
bool ScDPObject::GetHeaderLayout() const
{
return mbHeaderLayout;
}
void ScDPObject::SetOutRange(const ScRange& rRange)
{
aOutRange = rRange;
if ( pOutput )
pOutput->SetPosition( rRange.aStart );
}
void ScDPObject::SetSheetDesc(const ScSheetSourceDesc& rDesc, bool bFromRefUpdate)
{
if ( pSheetDesc && rDesc == *pSheetDesc )
return; // nothing to do
DELETEZ( pImpDesc );
DELETEZ( pServDesc );
delete pSheetDesc;
pSheetDesc = new ScSheetSourceDesc(rDesc);
// make valid QueryParam
pSheetDesc->aQueryParam.nCol1 = pSheetDesc->aSourceRange.aStart.Col();
pSheetDesc->aQueryParam.nRow1 = pSheetDesc->aSourceRange.aStart.Row();
pSheetDesc->aQueryParam.nCol2 = pSheetDesc->aSourceRange.aEnd.Col();
pSheetDesc->aQueryParam.nRow2 = pSheetDesc->aSourceRange.aEnd.Row();;
pSheetDesc->aQueryParam.bHasHeader = sal_True;
InvalidateSource(); // new source must be created
if (!bFromRefUpdate)
SetCacheId( -1 ); // #i116504# don't use the same cache ID for a different range (except reference update)
}
void ScDPObject::SetImportDesc(const ScImportSourceDesc& rDesc)
{
if ( pImpDesc && rDesc == *pImpDesc )
return; // nothing to do
DELETEZ( pSheetDesc );
DELETEZ( pServDesc );
delete pImpDesc;
pImpDesc = new ScImportSourceDesc(rDesc);
InvalidateSource(); // new source must be created
SetCacheId( -1 );
}
void ScDPObject::SetServiceData(const ScDPServiceDesc& rDesc)
{
if ( pServDesc && rDesc == *pServDesc )
return; // nothing to do
DELETEZ( pSheetDesc );
DELETEZ( pImpDesc );
delete pServDesc;
pServDesc = new ScDPServiceDesc(rDesc);
InvalidateSource(); // new source must be created
}
void ScDPObject::WriteSourceDataTo( ScDPObject& rDest ) const
{
if ( pSheetDesc )
rDest.SetSheetDesc( *pSheetDesc );
else if ( pImpDesc )
rDest.SetImportDesc( *pImpDesc );
else if ( pServDesc )
rDest.SetServiceData( *pServDesc );
// name/tag are not source data, but needed along with source data
rDest.aTableName = aTableName;
rDest.aTableTag = aTableTag;
}
void ScDPObject::WriteTempDataTo( ScDPObject& rDest ) const
{
rDest.nHeaderRows = nHeaderRows;
}
sal_Bool ScDPObject::IsSheetData() const
{
return ( pSheetDesc != NULL );
}
void ScDPObject::SetName(const String& rNew)
{
aTableName = rNew;
}
void ScDPObject::SetTag(const String& rNew)
{
aTableTag = rNew;
}
bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
{
if (!pSaveData)
return false;
long nDataDimCount = pSaveData->GetDataDimensionCount();
if (nDataDimCount != 1)
// There has to be exactly one data dimension for the description to
// appear at top-left corner.
return false;
CreateOutput();
ScRange aTabRange = pOutput->GetOutputRange(sheet::DataPilotOutputRangeType::TABLE);
return (rPos == aTabRange.aStart);
}
uno::Reference<sheet::XDimensionsSupplier> ScDPObject::GetSource()
{
CreateObjects();
return xSource;
}
void ScDPObject::CreateOutput()
{
CreateObjects();
if (!pOutput)
{
sal_Bool bFilterButton = IsSheetData() && pSaveData && pSaveData->GetFilterButton();
pOutput = new ScDPOutput( pDoc, xSource, aOutRange.aStart, bFilterButton );
pOutput->SetHeaderLayout ( mbHeaderLayout );
long nOldRows = nHeaderRows;
nHeaderRows = pOutput->GetHeaderRows();
if ( bAllowMove && nHeaderRows != nOldRows )
{
long nDiff = nOldRows - nHeaderRows;
if ( nOldRows == 0 )
--nDiff;
if ( nHeaderRows == 0 )
++nDiff;
long nNewRow = aOutRange.aStart.Row() + nDiff;
if ( nNewRow < 0 )
nNewRow = 0;
ScAddress aStart( aOutRange.aStart );
aStart.SetRow(nNewRow);
pOutput->SetPosition( aStart );
//! modify aOutRange?
bAllowMove = sal_False; // use only once
}
}
}
ScDPTableData* ScDPObject::GetTableData()
{
if (!mpTableData && !mbCreatingTableData)
{
// #i117239# While filling the cache, mpTableData is still null.
// Prevent nested calls from GetPivotData and similar functions.
mbCreatingTableData = true;
shared_ptr<ScDPTableData> pData;
if ( pImpDesc )
{
// database data
pData.reset(new ScDatabaseDPData(pDoc, *pImpDesc, GetCacheId()));
}
else
{
// cell data
if (!pSheetDesc)
{
DBG_ERROR("no source descriptor");
pSheetDesc = new ScSheetSourceDesc; // dummy defaults
}
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
pData.reset(new ScSheetDPData(pDoc, *pSheetDesc, GetCacheId()));
// End Comments
}
// grouping (for cell or database data)
if ( pSaveData && pSaveData->GetExistingDimensionData() )
{
shared_ptr<ScDPGroupTableData> pGroupData(new ScDPGroupTableData(pData, pDoc));
pSaveData->GetExistingDimensionData()->WriteToData(*pGroupData);
pData = pGroupData;
}
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
if ( pData )
SetCacheId( pData->GetCacheId()); // resets mpTableData
// End Comments
mpTableData = pData; // after SetCacheId
mbCreatingTableData = false;
}
return mpTableData.get();
}
void ScDPObject::CreateObjects()
{
// if groups are involved, create a new source with the ScDPGroupTableData
if ( bSettingsChanged && pSaveData && pSaveData->GetExistingDimensionData() )
InvalidateSource();
if (!xSource.is())
{
//! cache DPSource and/or Output?
DBG_ASSERT( bAlive, "CreateObjects on non-inserted DPObject" );
DELETEZ( pOutput ); // not valid when xSource is changed
if ( pServDesc )
{
xSource = CreateSource( *pServDesc );
}
if ( !xSource.is() ) // database or sheet data, or error in CreateSource
{
DBG_ASSERT( !pServDesc, "DPSource could not be created" );
ScDPTableData* pData = GetTableData();
if ( pData ) // nested GetTableData calls may return NULL
{
ScDPSource* pSource = new ScDPSource( pData );
xSource = pSource;
if ( pSaveData && bRefresh )
{
pSaveData->Refresh( xSource );
bRefresh = sal_False;
}
}
}
if ( xSource.is() && pSaveData )
pSaveData->WriteToSource( xSource );
}
else if (bSettingsChanged)
{
DELETEZ( pOutput ); // not valid when xSource is changed
uno::Reference<util::XRefreshable> xRef( xSource, uno::UNO_QUERY );
if (xRef.is())
{
try
{
xRef->refresh();
}
catch(uno::Exception&)
{
DBG_ERROR("exception in refresh");
}
}
if (pSaveData)
pSaveData->WriteToSource( xSource );
}
bSettingsChanged = sal_False;
}
void ScDPObject::InvalidateData()
{
bSettingsChanged = sal_True;
}
void ScDPObject::InvalidateSource()
{
Reference< XComponent > xObjectComp( xSource, UNO_QUERY );
if ( xObjectComp.is() )
{
try
{
xObjectComp->dispose();
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION();
}
}
xSource = NULL;
mpTableData.reset();
}
ScRange ScDPObject::GetNewOutputRange( sal_Bool& rOverflow )
{
CreateOutput(); // create xSource and pOutput if not already done
rOverflow = pOutput->HasError(); // range overflow or exception from source
if ( rOverflow )
return ScRange( aOutRange.aStart );
else
{
// don't store the result in aOutRange, because nothing has been output yet
return pOutput->GetOutputRange();
}
}
void ScDPObject::Output( const ScAddress& rPos )
{
// clear old output area
pDoc->DeleteAreaTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(),
aOutRange.aEnd.Col(), aOutRange.aEnd.Row(),
aOutRange.aStart.Tab(), IDF_ALL );
pDoc->RemoveFlagsTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(),
aOutRange.aEnd.Col(), aOutRange.aEnd.Row(),
aOutRange.aStart.Tab(), SC_MF_AUTO );
CreateOutput(); // create xSource and pOutput if not already done
pOutput->SetPosition( rPos );
pOutput->Output();
// aOutRange is always the range that was last output to the document
aOutRange = pOutput->GetOutputRange();
const ScAddress& s = aOutRange.aStart;
const ScAddress& e = aOutRange.aEnd;
pDoc->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), SC_MF_DP_TABLE);
}
const ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType )
{
CreateOutput();
if (pOutput->HasError())
return ScRange(aOutRange.aStart);
return pOutput->GetOutputRange(nType);
}
sal_Bool lcl_HasButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
{
return ((const ScMergeFlagAttr*)pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ))->HasButton();
}
void ScDPObject::RefreshAfterLoad()
{
// apply drop-down attribute, initialize nHeaderRows, without accessing the source
// (button attribute must be present)
// simple test: block of button cells at the top, followed by an empty cell
SCCOL nFirstCol = aOutRange.aStart.Col();
SCROW nFirstRow = aOutRange.aStart.Row();
SCTAB nTab = aOutRange.aStart.Tab();
SCROW nInitial = 0;
SCROW nOutRows = aOutRange.aEnd.Row() + 1 - aOutRange.aStart.Row();
while ( nInitial + 1 < nOutRows && lcl_HasButton( pDoc, nFirstCol, nFirstRow + nInitial, nTab ) )
++nInitial;
if ( nInitial + 1 < nOutRows &&
pDoc->IsBlockEmpty( nTab, nFirstCol, nFirstRow + nInitial, nFirstCol, nFirstRow + nInitial ) &&
aOutRange.aEnd.Col() > nFirstCol )
{
sal_Bool bFilterButton = IsSheetData(); // when available, filter button setting must be checked here
SCROW nSkip = bFilterButton ? 1 : 0;
for (SCROW nPos=nSkip; nPos<nInitial; nPos++)
pDoc->ApplyAttr( nFirstCol + 1, nFirstRow + nPos, nTab, ScMergeFlagAttr(SC_MF_AUTO) );
nHeaderRows = nInitial;
}
else
nHeaderRows = 0; // nothing found, no drop-down lists
}
void ScDPObject::BuildAllDimensionMembers()
{
if (!pSaveData)
return;
// #i111857# don't always create empty mpTableData for external service.
// #163781# Initialize all members from xSource instead.
if (pServDesc)
{
pSaveData->BuildAllDimensionMembersFromSource( this );
return;
}
pSaveData->BuildAllDimensionMembers(GetTableData());
}
bool ScDPObject::GetMemberNames( sal_Int32 nDim, Sequence<OUString>& rNames )
{
vector<ScDPLabelData::Member> aMembers;
if (!GetMembers(nDim, GetUsedHierarchy(nDim), aMembers))
return false;
size_t n = aMembers.size();
rNames.realloc(n);
for (size_t i = 0; i < n; ++i)
rNames[i] = aMembers[i].maName;
return true;
}
bool ScDPObject::GetMembers( sal_Int32 nDim, sal_Int32 nHier, vector<ScDPLabelData::Member>& rMembers )
{
Reference< container::XNameAccess > xMembersNA;
if (!GetMembersNA( nDim, nHier, xMembersNA ))
return false;
Reference<container::XIndexAccess> xMembersIA( new ScNameToIndexAccess(xMembersNA) );
sal_Int32 nCount = xMembersIA->getCount();
vector<ScDPLabelData::Member> aMembers;
aMembers.reserve(nCount);
for (sal_Int32 i = 0; i < nCount; ++i)
{
Reference<container::XNamed> xMember(xMembersIA->getByIndex(i), UNO_QUERY);
ScDPLabelData::Member aMem;
if (xMember.is())
aMem.maName = xMember->getName();
Reference<beans::XPropertySet> xMemProp(xMember, UNO_QUERY);
if (xMemProp.is())
{
aMem.mbVisible = ScUnoHelpFunctions::GetBoolProperty(xMemProp, OUString::createFromAscii(SC_UNO_ISVISIBL));
aMem.mbShowDetails = ScUnoHelpFunctions::GetBoolProperty(xMemProp, OUString::createFromAscii(SC_UNO_SHOWDETA));
aMem.maLayoutName = ScUnoHelpFunctions::GetStringProperty(
xMemProp, OUString::createFromAscii(SC_UNO_LAYOUTNAME), OUString());
}
aMembers.push_back(aMem);
}
rMembers.swap(aMembers);
return true;
}
void ScDPObject::UpdateReference( UpdateRefMode eUpdateRefMode,
const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
// Output area
SCCOL nCol1 = aOutRange.aStart.Col();
SCROW nRow1 = aOutRange.aStart.Row();
SCTAB nTab1 = aOutRange.aStart.Tab();
SCCOL nCol2 = aOutRange.aEnd.Col();
SCROW nRow2 = aOutRange.aEnd.Row();
SCTAB nTab2 = aOutRange.aEnd.Tab();
ScRefUpdateRes eRes =
ScRefUpdate::Update( pDoc, eUpdateRefMode,
rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
if ( eRes != UR_NOTHING )
SetOutRange( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) );
// sheet source data
if ( pSheetDesc )
{
nCol1 = pSheetDesc->aSourceRange.aStart.Col();
nRow1 = pSheetDesc->aSourceRange.aStart.Row();
nTab1 = pSheetDesc->aSourceRange.aStart.Tab();
nCol2 = pSheetDesc->aSourceRange.aEnd.Col();
nRow2 = pSheetDesc->aSourceRange.aEnd.Row();
nTab2 = pSheetDesc->aSourceRange.aEnd.Tab();
eRes = ScRefUpdate::Update( pDoc, eUpdateRefMode,
rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz,
nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
if ( eRes != UR_NOTHING )
{
ScSheetSourceDesc aNewDesc;
aNewDesc.aSourceRange = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
SCsCOL nDiffX = nCol1 - (SCsCOL) pSheetDesc->aSourceRange.aStart.Col();
SCsROW nDiffY = nRow1 - (SCsROW) pSheetDesc->aSourceRange.aStart.Row();
aNewDesc.aQueryParam = pSheetDesc->aQueryParam;
aNewDesc.aQueryParam.nCol1 = sal::static_int_cast<SCCOL>( aNewDesc.aQueryParam.nCol1 + nDiffX );
aNewDesc.aQueryParam.nCol2 = sal::static_int_cast<SCCOL>( aNewDesc.aQueryParam.nCol2 + nDiffX );
aNewDesc.aQueryParam.nRow1 += nDiffY; //! used?
aNewDesc.aQueryParam.nRow2 += nDiffY; //! used?
SCSIZE nEC = aNewDesc.aQueryParam.GetEntryCount();
for (SCSIZE i=0; i<nEC; i++)
if (aNewDesc.aQueryParam.GetEntry(i).bDoQuery)
aNewDesc.aQueryParam.GetEntry(i).nField += nDiffX;
SetSheetDesc( aNewDesc, true ); // allocates new pSheetDesc
}
}
}
sal_Bool ScDPObject::RefsEqual( const ScDPObject& r ) const
{
if ( aOutRange != r.aOutRange )
return sal_False;
if ( pSheetDesc && r.pSheetDesc )
{
if ( pSheetDesc->aSourceRange != r.pSheetDesc->aSourceRange )
return sal_False;
}
else if ( pSheetDesc || r.pSheetDesc )
{
DBG_ERROR("RefsEqual: SheetDesc set at only one object");
return sal_False;
}
return sal_True;
}
void ScDPObject::WriteRefsTo( ScDPObject& r ) const
{
r.SetOutRange( aOutRange );
if ( pSheetDesc )
r.SetSheetDesc( *pSheetDesc, true );
}
void ScDPObject::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData)
{
CreateOutput();
pOutput->GetPositionData(rPos, rPosData);
}
bool ScDPObject::GetDataFieldPositionData(
const ScAddress& rPos, Sequence<sheet::DataPilotFieldFilter>& rFilters)
{
CreateOutput();
vector<sheet::DataPilotFieldFilter> aFilters;
if (!pOutput->GetDataResultPositionData(aFilters, rPos))
return false;
sal_Int32 n = static_cast<sal_Int32>(aFilters.size());
rFilters.realloc(n);
for (sal_Int32 i = 0; i < n; ++i)
rFilters[i] = aFilters[i];
return true;
}
void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any> >& rTableData)
{
CreateOutput();
Reference<sheet::XDrillDownDataSupplier> xDrillDownData(xSource, UNO_QUERY);
if (!xDrillDownData.is())
return;
Sequence<sheet::DataPilotFieldFilter> filters;
if (!GetDataFieldPositionData(rPos, filters))
return;
rTableData = xDrillDownData->getDrillDownData(filters);
}
bool ScDPObject::IsDimNameInUse(const OUString& rName) const
{
if (!xSource.is())
return false;
Reference<container::XNameAccess> xDims = xSource->getDimensions();
Sequence<OUString> aDimNames = xDims->getElementNames();
sal_Int32 n = aDimNames.getLength();
for (sal_Int32 i = 0; i < n; ++i)
{
const OUString& rDimName = aDimNames[i];
if (rDimName.equalsIgnoreAsciiCase(rName))
return true;
Reference<beans::XPropertySet> xPropSet(xDims->getByName(rDimName), UNO_QUERY);
if (!xPropSet.is())
continue;
OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
xPropSet, OUString::createFromAscii(SC_UNO_LAYOUTNAME), OUString());
if (aLayoutName.equalsIgnoreAsciiCase(rName))
return true;
}
return false;
}
String ScDPObject::GetDimName( long nDim, sal_Bool& rIsDataLayout, sal_Int32* pFlags )
{
rIsDataLayout = sal_False;
String aRet;
if ( xSource.is() )
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
long nDimCount = xDims->getCount();
if ( nDim < nDimCount )
{
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
if ( xDimName.is() && xDimProp.is() )
{
sal_Bool bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
//! error checking -- is "IsDataLayoutDimension" property required??
rtl::OUString aName;
try
{
aName = xDimName->getName();
}
catch(uno::Exception&)
{
}
if ( bData )
rIsDataLayout = sal_True;
else
aRet = String( aName );
if (pFlags)
*pFlags = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(SC_UNO_FLAGS), 0 );
}
}
}
return aRet;
}
sal_Bool ScDPObject::IsDuplicated( long nDim )
{
sal_Bool bDuplicated = sal_False;
if ( xSource.is() )
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
long nDimCount = xDims->getCount();
if ( nDim < nDimCount )
{
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
if ( xDimProp.is() )
{
try
{
uno::Any aOrigAny = xDimProp->getPropertyValue(
rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) );
uno::Reference<uno::XInterface> xIntOrig;
if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() )
bDuplicated = sal_True;
}
catch(uno::Exception&)
{
}
}
}
}
return bDuplicated;
}
long ScDPObject::GetDimCount()
{
long nRet = 0;
if ( xSource.is() )
{
try
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
if ( xDimsName.is() )
nRet = xDimsName->getElementNames().getLength();
}
catch(uno::Exception&)
{
}
}
return nRet;
}
void ScDPObject::FillPageList( TypedScStrCollection& rStrings, long nField )
{
//! merge members access with ToggleDetails?
//! convert field index to dimension index?
DBG_ASSERT( xSource.is(), "no source" );
if ( !xSource.is() ) return;
uno::Reference<container::XNamed> xDim;
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
long nIntCount = xIntDims->getCount();
if ( nField < nIntCount )
{
uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
xIntDims->getByIndex(nField) );
xDim = uno::Reference<container::XNamed>( xIntDim, uno::UNO_QUERY );
}
DBG_ASSERT( xDim.is(), "dimension not found" );
if ( !xDim.is() ) return;
uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
long nLevel = 0;
long nHierCount = 0;
uno::Reference<container::XIndexAccess> xHiers;
uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
if ( xHierSupp.is() )
{
uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
xHiers = new ScNameToIndexAccess( xHiersName );
nHierCount = xHiers->getCount();
}
uno::Reference<uno::XInterface> xHier;
if ( nHierarchy < nHierCount )
xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex(nHierarchy) );
DBG_ASSERT( xHier.is(), "hierarchy not found" );
if ( !xHier.is() ) return;
long nLevCount = 0;
uno::Reference<container::XIndexAccess> xLevels;
uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
if ( xLevSupp.is() )
{
uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
xLevels = new ScNameToIndexAccess( xLevsName );
nLevCount = xLevels->getCount();
}
uno::Reference<uno::XInterface> xLevel;
if ( nLevel < nLevCount )
xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex(nLevel) );
DBG_ASSERT( xLevel.is(), "level not found" );
if ( !xLevel.is() ) return;
uno::Reference<container::XNameAccess> xMembers;
uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY );
if ( xMbrSupp.is() )
xMembers = xMbrSupp->getMembers();
DBG_ASSERT( xMembers.is(), "members not found" );
if ( !xMembers.is() ) return;
uno::Sequence<rtl::OUString> aNames = xMembers->getElementNames();
long nNameCount = aNames.getLength();
const rtl::OUString* pNameArr = aNames.getConstArray();
for (long nPos = 0; nPos < nNameCount; ++nPos)
{
// Make sure to insert only visible members.
Reference<XPropertySet> xPropSet(xMembers->getByName(pNameArr[nPos]), UNO_QUERY);
sal_Bool bVisible = false;
if (xPropSet.is())
{
Any any = xPropSet->getPropertyValue(OUString::createFromAscii(SC_UNO_ISVISIBL));
any >>= bVisible;
}
if (bVisible)
{
// use the order from getElementNames
TypedStrData* pData = new TypedStrData( pNameArr[nPos] );
if ( !rStrings.AtInsert( rStrings.GetCount(), pData ) )
delete pData;
}
}
// add "-all-" entry to the top (unsorted)
TypedStrData* pAllData = new TypedStrData( String( ScResId( SCSTR_ALL ) ) ); //! separate string? (also output)
if ( !rStrings.AtInsert( 0, pAllData ) )
delete pAllData;
}
void ScDPObject::GetHeaderPositionData(const ScAddress& rPos, DataPilotTableHeaderData& rData)
{
using namespace ::com::sun::star::sheet::DataPilotTablePositionType;
CreateOutput(); // create xSource and pOutput if not already done
// Reset member values to invalid state.
rData.Dimension = rData.Hierarchy = rData.Level = -1;
rData.Flags = 0;
DataPilotTablePositionData aPosData;
pOutput->GetPositionData(rPos, aPosData);
const sal_Int32 nPosType = aPosData.PositionType;
if (nPosType == COLUMN_HEADER || nPosType == ROW_HEADER)
aPosData.PositionData >>= rData;
}
// Returns sal_True on success and stores the result in rTarget
sal_Bool ScDPObject::GetPivotData( ScDPGetPivotDataField& rTarget,
const std::vector< ScDPGetPivotDataField >& rFilters )
{
// #i117239# Exit with an error if called from creating the cache for this object
// (don't create an empty pOutput object)
if (mbCreatingTableData)
return sal_False;
CreateOutput(); // create xSource and pOutput if not already done
return pOutput->GetPivotData( rTarget, rFilters );
}
sal_Bool ScDPObject::IsFilterButton( const ScAddress& rPos )
{
CreateOutput(); // create xSource and pOutput if not already done
return pOutput->IsFilterButton( rPos );
}
long ScDPObject::GetHeaderDim( const ScAddress& rPos, sal_uInt16& rOrient )
{
CreateOutput(); // create xSource and pOutput if not already done
return pOutput->GetHeaderDim( rPos, rOrient );
}
sal_Bool ScDPObject::GetHeaderDrag( const ScAddress& rPos, sal_Bool bMouseLeft, sal_Bool bMouseTop, long nDragDim,
Rectangle& rPosRect, sal_uInt16& rOrient, long& rDimPos )
{
CreateOutput(); // create xSource and pOutput if not already done
return pOutput->GetHeaderDrag( rPos, bMouseLeft, bMouseTop, nDragDim, rPosRect, rOrient, rDimPos );
}
void ScDPObject::GetMemberResultNames( ScStrCollection& rNames, long nDimension )
{
CreateOutput(); // create xSource and pOutput if not already done
pOutput->GetMemberResultNames( rNames, nDimension ); // used only with table data -> level not needed
}
bool lcl_Dequote( const String& rSource, xub_StrLen nStartPos, xub_StrLen& rEndPos, String& rResult )
{
// nStartPos has to point to opening quote
bool bRet = false;
const sal_Unicode cQuote = '\'';
if ( rSource.GetChar(nStartPos) == cQuote )
{
rtl::OUStringBuffer aBuffer;
xub_StrLen nPos = nStartPos + 1;
const xub_StrLen nLen = rSource.Len();
while ( nPos < nLen )
{
const sal_Unicode cNext = rSource.GetChar(nPos);
if ( cNext == cQuote )
{
if ( nPos+1 < nLen && rSource.GetChar(nPos+1) == cQuote )
{
// double quote is used for an embedded quote
aBuffer.append( cNext ); // append one quote
++nPos; // skip the next one
}
else
{
// end of quoted string
rResult = aBuffer.makeStringAndClear();
rEndPos = nPos + 1; // behind closing quote
return true;
}
}
else
aBuffer.append( cNext );
++nPos;
}
// no closing quote before the end of the string -> error (bRet still false)
}
return bRet;
}
struct ScGetPivotDataFunctionEntry
{
const sal_Char* pName;
sheet::GeneralFunction eFunc;
};
bool lcl_ParseFunction( const String& rList, xub_StrLen nStartPos, xub_StrLen& rEndPos, sheet::GeneralFunction& rFunc )
{
static const ScGetPivotDataFunctionEntry aFunctions[] =
{
// our names
{ "Sum", sheet::GeneralFunction_SUM },
{ "Count", sheet::GeneralFunction_COUNT },
{ "Average", sheet::GeneralFunction_AVERAGE },
{ "Max", sheet::GeneralFunction_MAX },
{ "Min", sheet::GeneralFunction_MIN },
{ "Product", sheet::GeneralFunction_PRODUCT },
{ "CountNums", sheet::GeneralFunction_COUNTNUMS },
{ "StDev", sheet::GeneralFunction_STDEV },
{ "StDevp", sheet::GeneralFunction_STDEVP },
{ "Var", sheet::GeneralFunction_VAR },
{ "VarP", sheet::GeneralFunction_VARP },
// compatibility names
{ "Count Nums", sheet::GeneralFunction_COUNTNUMS },
{ "StdDev", sheet::GeneralFunction_STDEV },
{ "StdDevp", sheet::GeneralFunction_STDEVP }
};
const xub_StrLen nListLen = rList.Len();
while ( nStartPos < nListLen && rList.GetChar(nStartPos) == ' ' )
++nStartPos;
bool bParsed = false;
bool bFound = false;
String aFuncStr;
xub_StrLen nFuncEnd = 0;
if ( nStartPos < nListLen && rList.GetChar(nStartPos) == '\'' )
bParsed = lcl_Dequote( rList, nStartPos, nFuncEnd, aFuncStr );
else
{
nFuncEnd = rList.Search( static_cast<sal_Unicode>(']'), nStartPos );
if ( nFuncEnd != STRING_NOTFOUND )
{
aFuncStr = rList.Copy( nStartPos, nFuncEnd - nStartPos );
bParsed = true;
}
}
if ( bParsed )
{
aFuncStr.EraseLeadingAndTrailingChars( ' ' );
const sal_Int32 nFuncCount = sizeof(aFunctions) / sizeof(aFunctions[0]);
for ( sal_Int32 nFunc=0; nFunc<nFuncCount && !bFound; nFunc++ )
{
if ( aFuncStr.EqualsIgnoreCaseAscii( aFunctions[nFunc].pName ) )
{
rFunc = aFunctions[nFunc].eFunc;
bFound = true;
while ( nFuncEnd < nListLen && rList.GetChar(nFuncEnd) == ' ' )
++nFuncEnd;
rEndPos = nFuncEnd;
}
}
}
return bFound;
}
bool lcl_IsAtStart( const String& rList, const String& rSearch, sal_Int32& rMatched,
bool bAllowBracket, sheet::GeneralFunction* pFunc )
{
sal_Int32 nMatchList = 0;
sal_Int32 nMatchSearch = 0;
sal_Unicode cFirst = rList.GetChar(0);
if ( cFirst == '\'' || cFirst == '[' )
{
// quoted string or string in brackets must match completely
String aDequoted;
xub_StrLen nQuoteEnd = 0;
bool bParsed = false;
if ( cFirst == '\'' )
bParsed = lcl_Dequote( rList, 0, nQuoteEnd, aDequoted );
else if ( cFirst == '[' )
{
// skip spaces after the opening bracket
xub_StrLen nStartPos = 1;
const xub_StrLen nListLen = rList.Len();
while ( nStartPos < nListLen && rList.GetChar(nStartPos) == ' ' )
++nStartPos;
if ( rList.GetChar(nStartPos) == '\'' ) // quoted within the brackets?
{
if ( lcl_Dequote( rList, nStartPos, nQuoteEnd, aDequoted ) )
{
// after the quoted string, there must be the closing bracket, optionally preceded by spaces,
// and/or a function name
while ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ' ' )
++nQuoteEnd;
// semicolon separates function name
if ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ';' && pFunc )
{
xub_StrLen nFuncEnd = 0;
if ( lcl_ParseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) )
nQuoteEnd = nFuncEnd;
}
if ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ']' )
{
++nQuoteEnd; // include the closing bracket for the matched length
bParsed = true;
}
}
}
else
{
// implicit quoting to the closing bracket
xub_StrLen nClosePos = rList.Search( static_cast<sal_Unicode>(']'), nStartPos );
if ( nClosePos != STRING_NOTFOUND )
{
xub_StrLen nNameEnd = nClosePos;
xub_StrLen nSemiPos = rList.Search( static_cast<sal_Unicode>(';'), nStartPos );
if ( nSemiPos != STRING_NOTFOUND && nSemiPos < nClosePos && pFunc )
{
xub_StrLen nFuncEnd = 0;
if ( lcl_ParseFunction( rList, nSemiPos + 1, nFuncEnd, *pFunc ) )
nNameEnd = nSemiPos;
}
aDequoted = rList.Copy( nStartPos, nNameEnd - nStartPos );
aDequoted.EraseTrailingChars( ' ' ); // spaces before the closing bracket or semicolon
nQuoteEnd = nClosePos + 1;
bParsed = true;
}
}
}
if ( bParsed && ScGlobal::GetpTransliteration()->isEqual( aDequoted, rSearch ) )
{
nMatchList = nQuoteEnd; // match count in the list string, including quotes
nMatchSearch = rSearch.Len();
}
}
else
{
// otherwise look for search string at the start of rList
ScGlobal::GetpTransliteration()->equals( rList, 0, rList.Len(), nMatchList,
rSearch, 0, rSearch.Len(), nMatchSearch );
}
if ( nMatchSearch == rSearch.Len() )
{
// search string is at start of rList - look for following space or end of string
bool bValid = false;
if ( sal::static_int_cast<xub_StrLen>(nMatchList) >= rList.Len() )
bValid = true;
else
{
sal_Unicode cNext = rList.GetChar(sal::static_int_cast<xub_StrLen>(nMatchList));
if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) )
bValid = true;
}
if ( bValid )
{
rMatched = nMatchList;
return true;
}
}
return false;
}
sal_Bool ScDPObject::ParseFilters( ScDPGetPivotDataField& rTarget,
std::vector< ScDPGetPivotDataField >& rFilters,
const String& rFilterList )
{
// parse the string rFilterList into parameters for GetPivotData
CreateObjects(); // create xSource if not already done
std::vector<String> aDataNames; // data fields (source name)
std::vector<String> aGivenNames; // data fields (compound name)
std::vector<String> aFieldNames; // column/row/data fields
std::vector< uno::Sequence<rtl::OUString> > aFieldValues;
//
// get all the field and item names
//
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
sal_Int32 nDimCount = xIntDims->getCount();
for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ )
{
uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nDim) );
uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY );
uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY );
sal_Bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
sal_Int32 nOrient = ScUnoHelpFunctions::GetEnumProperty(
xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
sheet::DataPilotFieldOrientation_HIDDEN );
if ( !bDataLayout )
{
if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
{
String aSourceName;
String aGivenName;
ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim );
aDataNames.push_back( aSourceName );
aGivenNames.push_back( aGivenName );
}
else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN )
{
// get level names, as in ScDPOutput
uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
if ( nHierarchy >= xHiers->getCount() )
nHierarchy = 0;
uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface(
xHiers->getByIndex(nHierarchy) );
uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
if ( xHierSupp.is() )
{
uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
sal_Int32 nLevCount = xLevels->getCount();
for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
{
uno::Reference<uno::XInterface> xLevel = ScUnoHelpFunctions::AnyToInterface(
xLevels->getByIndex(nLev) );
uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY );
uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY );
if ( xLevNam.is() && xLevSupp.is() )
{
uno::Reference<container::XNameAccess> xMembers = xLevSupp->getMembers();
String aFieldName( xLevNam->getName() );
uno::Sequence<rtl::OUString> aMemberNames( xMembers->getElementNames() );
aFieldNames.push_back( aFieldName );
aFieldValues.push_back( aMemberNames );
}
}
}
}
}
}
//
// compare and build filters
//
SCSIZE nDataFields = aDataNames.size();
SCSIZE nFieldCount = aFieldNames.size();
DBG_ASSERT( aGivenNames.size() == nDataFields && aFieldValues.size() == nFieldCount, "wrong count" );
bool bError = false;
bool bHasData = false;
String aRemaining( rFilterList );
aRemaining.EraseLeadingAndTrailingChars( ' ' );
while ( aRemaining.Len() && !bError )
{
bool bUsed = false;
// look for data field name
for ( SCSIZE nDataPos=0; nDataPos<nDataFields && !bUsed; nDataPos++ )
{
String aFound;
sal_Int32 nMatched = 0;
if ( lcl_IsAtStart( aRemaining, aDataNames[nDataPos], nMatched, false, NULL ) )
aFound = aDataNames[nDataPos];
else if ( lcl_IsAtStart( aRemaining, aGivenNames[nDataPos], nMatched, false, NULL ) )
aFound = aGivenNames[nDataPos];
if ( aFound.Len() )
{
rTarget.maFieldName = aFound;
aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) );
bHasData = true;
bUsed = true;
}
}
// look for field name
String aSpecField;
bool bHasFieldName = false;
if ( !bUsed )
{
sal_Int32 nMatched = 0;
for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ )
{
if ( lcl_IsAtStart( aRemaining, aFieldNames[nField], nMatched, true, NULL ) )
{
aSpecField = aFieldNames[nField];
aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) );
aRemaining.EraseLeadingChars( ' ' );
// field name has to be followed by item name in brackets
if ( aRemaining.GetChar(0) == '[' )
{
bHasFieldName = true;
// bUsed remains false - still need the item
}
else
{
bUsed = true;
bError = true;
}
}
}
}
// look for field item
if ( !bUsed )
{
bool bItemFound = false;
sal_Int32 nMatched = 0;
String aFoundName;
String aFoundValue;
sheet::GeneralFunction eFunc = sheet::GeneralFunction_NONE;
sheet::GeneralFunction eFoundFunc = sheet::GeneralFunction_NONE;
for ( SCSIZE nField=0; nField<nFieldCount; nField++ )
{
// If a field name is given, look in that field only, otherwise in all fields.
// aSpecField is initialized from aFieldNames array, so exact comparison can be used.
if ( !bHasFieldName || aFieldNames[nField] == aSpecField )
{
const uno::Sequence<rtl::OUString>& rItems = aFieldValues[nField];
sal_Int32 nItemCount = rItems.getLength();
const rtl::OUString* pItemArr = rItems.getConstArray();
for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ )
{
if ( lcl_IsAtStart( aRemaining, pItemArr[nItem], nMatched, false, &eFunc ) )
{
if ( bItemFound )
bError = true; // duplicate (also across fields)
else
{
aFoundName = aFieldNames[nField];
aFoundValue = pItemArr[nItem];
eFoundFunc = eFunc;
bItemFound = true;
bUsed = true;
}
}
}
}
}
if ( bItemFound && !bError )
{
ScDPGetPivotDataField aField;
aField.maFieldName = aFoundName;
aField.meFunction = eFoundFunc;
aField.mbValIsStr = true;
aField.maValStr = aFoundValue;
aField.mnValNum = 0.0;
rFilters.push_back( aField );
aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) );
}
}
if ( !bUsed )
bError = true;
aRemaining.EraseLeadingChars( ' ' ); // remove any number of spaces between entries
}
if ( !bError && !bHasData && aDataNames.size() == 1 )
{
// if there's only one data field, its name need not be specified
rTarget.maFieldName = aDataNames[0];
bHasData = true;
}
return bHasData && !bError;
}
void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj)
{
CreateObjects(); // create xSource if not already done
// find dimension name
uno::Reference<container::XNamed> xDim;
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName );
long nIntCount = xIntDims->getCount();
if ( rElemDesc.Dimension < nIntCount )
{
uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface(
xIntDims->getByIndex(rElemDesc.Dimension) );
xDim = uno::Reference<container::XNamed>( xIntDim, uno::UNO_QUERY );
}
DBG_ASSERT( xDim.is(), "dimension not found" );
if ( !xDim.is() ) return;
String aDimName = xDim->getName();
uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY );
sal_Bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
if (bDataLayout)
{
// the elements of the data layout dimension can't be found by their names
// -> don't change anything
return;
}
// query old state
long nHierCount = 0;
uno::Reference<container::XIndexAccess> xHiers;
uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY );
if ( xHierSupp.is() )
{
uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies();
xHiers = new ScNameToIndexAccess( xHiersName );
nHierCount = xHiers->getCount();
}
uno::Reference<uno::XInterface> xHier;
if ( rElemDesc.Hierarchy < nHierCount )
xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex(rElemDesc.Hierarchy) );
DBG_ASSERT( xHier.is(), "hierarchy not found" );
if ( !xHier.is() ) return;
long nLevCount = 0;
uno::Reference<container::XIndexAccess> xLevels;
uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY );
if ( xLevSupp.is() )
{
uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels();
xLevels = new ScNameToIndexAccess( xLevsName );
nLevCount = xLevels->getCount();
}
uno::Reference<uno::XInterface> xLevel;
if ( rElemDesc.Level < nLevCount )
xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex(rElemDesc.Level) );
DBG_ASSERT( xLevel.is(), "level not found" );
if ( !xLevel.is() ) return;
uno::Reference<container::XNameAccess> xMembers;
uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY );
if ( xMbrSupp.is() )
xMembers = xMbrSupp->getMembers();
sal_Bool bFound = sal_False;
sal_Bool bShowDetails = sal_True;
if ( xMembers.is() )
{
if ( xMembers->hasByName(rElemDesc.MemberName) )
{
uno::Reference<uno::XInterface> xMemberInt = ScUnoHelpFunctions::AnyToInterface(
xMembers->getByName(rElemDesc.MemberName) );
uno::Reference<beans::XPropertySet> xMbrProp( xMemberInt, uno::UNO_QUERY );
if ( xMbrProp.is() )
{
bShowDetails = ScUnoHelpFunctions::GetBoolProperty( xMbrProp,
rtl::OUString::createFromAscii(DP_PROP_SHOWDETAILS) );
//! don't set bFound if property is unknown?
bFound = sal_True;
}
}
}
DBG_ASSERT( bFound, "member not found" );
//! use Hierarchy and Level in SaveData !!!!
// modify pDestObj if set, this object otherwise
ScDPSaveData* pModifyData = pDestObj ? ( pDestObj->pSaveData ) : pSaveData;
DBG_ASSERT( pModifyData, "no data?" );
if ( pModifyData )
{
const String aName = rElemDesc.MemberName;
pModifyData->GetDimensionByName(aDimName)->
GetMemberByName(aName)->SetShowDetails( !bShowDetails ); // toggle
if ( pDestObj )
pDestObj->InvalidateData(); // re-init source from SaveData
else
InvalidateData(); // re-init source from SaveData
}
}
long lcl_FindName( const rtl::OUString& rString, const uno::Reference<container::XNameAccess>& xCollection )
{
if ( xCollection.is() )
{
uno::Sequence<rtl::OUString> aSeq = xCollection->getElementNames();
long nCount = aSeq.getLength();
const rtl::OUString* pArr = aSeq.getConstArray();
for (long nPos=0; nPos<nCount; nPos++)
if ( pArr[nPos] == rString )
return nPos;
}
return -1; // not found
}
sal_uInt16 lcl_FirstSubTotal( const uno::Reference<beans::XPropertySet>& xDimProp ) // PIVOT_FUNC mask
{
uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
if ( xDimProp.is() && xDimSupp.is() )
{
uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
if ( nHierarchy >= xHiers->getCount() )
nHierarchy = 0;
uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface(
xHiers->getByIndex(nHierarchy) );
uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
if ( xHierSupp.is() )
{
uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
uno::Reference<uno::XInterface> xLevel =
ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( 0 ) );
uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
if ( xLevProp.is() )
{
uno::Any aSubAny;
try
{
aSubAny = xLevProp->getPropertyValue(
rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) );
}
catch(uno::Exception&)
{
}
uno::Sequence<sheet::GeneralFunction> aSeq;
if ( aSubAny >>= aSeq )
{
sal_uInt16 nMask = 0;
const sheet::GeneralFunction* pArray = aSeq.getConstArray();
long nCount = aSeq.getLength();
for (long i=0; i<nCount; i++)
nMask |= ScDataPilotConversion::FunctionBit(pArray[i]);
return nMask;
}
}
}
}
DBG_ERROR("FirstSubTotal: NULL");
return 0;
}
sal_uInt16 lcl_CountBits( sal_uInt16 nBits )
{
if (!nBits) return 0;
sal_uInt16 nCount = 0;
sal_uInt16 nMask = 1;
for (sal_uInt16 i=0; i<16; i++)
{
if ( nBits & nMask )
++nCount;
nMask <<= 1;
}
return nCount;
}
void lcl_FillOldFields( ScPivotFieldVector& rFields,
const uno::Reference<sheet::XDimensionsSupplier>& xSource,
sal_uInt16 nOrient, SCCOL nColAdd, bool bAddData )
{
bool bDataFound = false;
rFields.clear();
//! merge multiple occurences (data field with different functions)
//! force data field in one dimension
std::vector< long > aPos;
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
long nDimCount = xDims->getCount();
for (long nDim=0; nDim < nDimCount; nDim++)
{
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
long nDimOrient = ScUnoHelpFunctions::GetEnumProperty(
xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION),
sheet::DataPilotFieldOrientation_HIDDEN );
if ( xDimProp.is() && nDimOrient == nOrient )
{
sal_uInt16 nMask = 0;
if ( nOrient == sheet::DataPilotFieldOrientation_DATA )
{
sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty(
xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION),
sheet::GeneralFunction_NONE );
if ( eFunc == sheet::GeneralFunction_AUTO )
{
//! test for numeric data
eFunc = sheet::GeneralFunction_SUM;
}
nMask = ScDataPilotConversion::FunctionBit(eFunc);
}
else
nMask = lcl_FirstSubTotal( xDimProp ); // from first hierarchy
sal_Bool bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
uno::Any aOrigAny;
try
{
aOrigAny = xDimProp->getPropertyValue(
rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) );
}
catch(uno::Exception&)
{
}
long nDupSource = -1;
uno::Reference<uno::XInterface> xIntOrig = ScUnoHelpFunctions::AnyToInterface( aOrigAny );
if ( xIntOrig.is() )
{
uno::Reference<container::XNamed> xNameOrig( xIntOrig, uno::UNO_QUERY );
if ( xNameOrig.is() )
nDupSource = lcl_FindName( xNameOrig->getName(), xDimsName );
}
bool bDupUsed = false;
if ( nDupSource >= 0 )
{
// add function bit to previous entry
SCsCOL nCompCol;
if ( bDataLayout )
nCompCol = PIVOT_DATA_FIELD;
else
nCompCol = static_cast<SCsCOL>(nDupSource)+nColAdd; //! seek source column from name
for (ScPivotFieldVector::iterator aIt = rFields.begin(), aEnd = rFields.end(); (aIt != aEnd) && !bDupUsed; ++aIt)
if ( aIt->nCol == nCompCol )
{
// add to previous column only if new bits aren't already set there
if ( ( aIt->nFuncMask & nMask ) == 0 )
{
aIt->nFuncMask |= nMask;
aIt->nFuncCount = lcl_CountBits( aIt->nFuncMask );
bDupUsed = true;
}
}
}
if ( !bDupUsed ) // also for duplicated dim if original has different orientation
{
rFields.resize( rFields.size() + 1 );
ScPivotField& rField = rFields.back();
if ( bDataLayout )
{
rField.nCol = PIVOT_DATA_FIELD;
bDataFound = true;
}
else if ( nDupSource >= 0 ) // if source was not found (different orientation)
rField.nCol = static_cast<SCsCOL>(nDupSource)+nColAdd; //! seek from name
else
rField.nCol = static_cast<SCsCOL>(nDim)+nColAdd; //! seek source column from name
rField.nFuncMask = nMask;
rField.nFuncCount = lcl_CountBits( nMask );
aPos.push_back( ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_POSITION) ) );
try
{
if( nOrient == sheet::DataPilotFieldOrientation_DATA )
xDimProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_REFVALUE ) ) )
>>= rFields.back().maFieldRef;
}
catch( uno::Exception& )
{
}
}
}
}
// sort by getPosition() value
size_t nSize = aPos.size();
for (size_t i=0; i+1<nSize; i++)
{
for (size_t j=0; j+i+1<nSize; j++)
if ( aPos[j+1] < aPos[j] )
{
std::swap( aPos[j], aPos[j+1] );
std::swap( rFields[j], rFields[j+1] );
}
}
if ( bAddData && !bDataFound )
{
rFields.resize( rFields.size() + 1 );
ScPivotField& rField = rFields.back();
rField.nCol = PIVOT_DATA_FIELD;
rField.nFuncMask = 0;
rField.nFuncCount = 0;
}
}
sal_Bool ScDPObject::FillOldParam(ScPivotParam& rParam) const
{
((ScDPObject*)this)->CreateObjects(); // xSource is needed for field numbers
rParam.nCol = aOutRange.aStart.Col();
rParam.nRow = aOutRange.aStart.Row();
rParam.nTab = aOutRange.aStart.Tab();
// ppLabelArr / nLabels is not changed
SCCOL nColAdd = 0;
bool bAddData = ( lcl_GetDataGetOrientation( xSource ) == sheet::DataPilotFieldOrientation_HIDDEN );
lcl_FillOldFields( rParam.maPageArr, xSource, sheet::DataPilotFieldOrientation_PAGE, nColAdd, false );
lcl_FillOldFields( rParam.maColArr, xSource, sheet::DataPilotFieldOrientation_COLUMN, nColAdd, bAddData );
lcl_FillOldFields( rParam.maRowArr, xSource, sheet::DataPilotFieldOrientation_ROW, nColAdd, false );
lcl_FillOldFields( rParam.maDataArr, xSource, sheet::DataPilotFieldOrientation_DATA, nColAdd, false );
uno::Reference<beans::XPropertySet> xProp( xSource, uno::UNO_QUERY );
if (xProp.is())
{
try
{
rParam.bMakeTotalCol = ScUnoHelpFunctions::GetBoolProperty( xProp,
rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND), sal_True );
rParam.bMakeTotalRow = ScUnoHelpFunctions::GetBoolProperty( xProp,
rtl::OUString::createFromAscii(DP_PROP_ROWGRAND), sal_True );
// following properties may be missing for external sources
rParam.bIgnoreEmptyRows = ScUnoHelpFunctions::GetBoolProperty( xProp,
rtl::OUString::createFromAscii(DP_PROP_IGNOREEMPTY) );
rParam.bDetectCategories = ScUnoHelpFunctions::GetBoolProperty( xProp,
rtl::OUString::createFromAscii(DP_PROP_REPEATIFEMPTY) );
}
catch(uno::Exception&)
{
// no error
}
}
return sal_True;
}
void lcl_FillLabelData( ScDPLabelData& rData, const uno::Reference< beans::XPropertySet >& xDimProp )
{
uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY );
if ( xDimProp.is() && xDimSupp.is() )
{
uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() );
long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) );
if ( nHierarchy >= xHiers->getCount() )
nHierarchy = 0;
rData.mnUsedHier = nHierarchy;
uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface(
xHiers->getByIndex(nHierarchy) );
uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY );
if ( xHierSupp.is() )
{
uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() );
uno::Reference<uno::XInterface> xLevel =
ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( 0 ) );
uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY );
if ( xLevProp.is() )
{
rData.mbShowAll = ScUnoHelpFunctions::GetBoolProperty( xLevProp,
rtl::OUString::createFromAscii(DP_PROP_SHOWEMPTY) );
try
{
xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_SORTING ) ) )
>>= rData.maSortInfo;
xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_LAYOUT ) ) )
>>= rData.maLayoutInfo;
xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_AUTOSHOW ) ) )
>>= rData.maShowInfo;
}
catch(uno::Exception&)
{
}
}
}
}
}
sal_Bool ScDPObject::FillLabelData(ScPivotParam& rParam)
{
rParam.maLabelArray.clear();
((ScDPObject*)this)->CreateObjects();
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
long nDimCount = xDims->getCount();
if ( nDimCount > MAX_LABELS )
nDimCount = MAX_LABELS;
if (!nDimCount)
return sal_False;
for (long nDim=0; nDim < nDimCount; nDim++)
{
String aFieldName;
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY );
if ( xDimName.is() && xDimProp.is() )
{
sal_Bool bDuplicated = sal_False;
sal_Bool bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp,
rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) );
//! error checking -- is "IsDataLayoutDimension" property required??
try
{
aFieldName = String( xDimName->getName() );
uno::Any aOrigAny = xDimProp->getPropertyValue(
rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) );
uno::Reference<uno::XInterface> xIntOrig;
if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() )
bDuplicated = sal_True;
}
catch(uno::Exception&)
{
}
OUString aLayoutName = ScUnoHelpFunctions::GetStringProperty(
xDimProp, OUString::createFromAscii(SC_UNO_LAYOUTNAME), OUString());
if ( aFieldName.Len() && !bData && !bDuplicated )
{
SCsCOL nCol = static_cast< SCsCOL >( nDim ); //! ???
bool bIsValue = true; //! check
ScDPLabelData aNewLabel(aFieldName, nCol, bIsValue);
aNewLabel.maLayoutName = aLayoutName;
GetHierarchies(nDim, aNewLabel.maHiers);
GetMembers(nDim, GetUsedHierarchy(nDim), aNewLabel.maMembers);
lcl_FillLabelData(aNewLabel, xDimProp);
aNewLabel.mnFlags = ScUnoHelpFunctions::GetLongProperty( xDimProp,
rtl::OUString::createFromAscii(SC_UNO_FLAGS), 0 );
rParam.maLabelArray.push_back(aNewLabel);
}
}
}
return sal_True;
}
sal_Bool ScDPObject::GetHierarchiesNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xHiers )
{
sal_Bool bRet = sal_False;
uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
if( xIntDims.is() )
{
uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
if (xHierSup.is())
{
xHiers.set( xHierSup->getHierarchies() );
bRet = xHiers.is();
}
}
return bRet;
}
sal_Bool ScDPObject::GetHierarchies( sal_Int32 nDim, uno::Sequence< rtl::OUString >& rHiers )
{
sal_Bool bRet = sal_False;
uno::Reference< container::XNameAccess > xHiersNA;
if( GetHierarchiesNA( nDim, xHiersNA ) )
{
rHiers = xHiersNA->getElementNames();
bRet = sal_True;
}
return bRet;
}
sal_Int32 ScDPObject::GetUsedHierarchy( sal_Int32 nDim )
{
sal_Int32 nHier = 0;
uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
if (xDim.is())
nHier = ScUnoHelpFunctions::GetLongProperty( xDim, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( SC_UNO_USEDHIER ) ) );
return nHier;
}
sal_Bool ScDPObject::GetMembersNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xMembers )
{
return GetMembersNA( nDim, GetUsedHierarchy( nDim ), xMembers );
}
sal_Bool ScDPObject::GetMembersNA( sal_Int32 nDim, sal_Int32 nHier, uno::Reference< container::XNameAccess >& xMembers )
{
sal_Bool bRet = sal_False;
uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() );
uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName ));
uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY);
if (xDim.is())
{
uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xDim, uno::UNO_QUERY);
if (xHierSup.is())
{
uno::Reference<container::XIndexAccess> xHiers(new ScNameToIndexAccess(xHierSup->getHierarchies()));
uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHiers->getByIndex(nHier), uno::UNO_QUERY );
if ( xLevSupp.is() )
{
uno::Reference<container::XIndexAccess> xLevels(new ScNameToIndexAccess( xLevSupp->getLevels()));
if (xLevels.is())
{
sal_Int32 nLevCount = xLevels->getCount();
if (nLevCount > 0)
{
uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevels->getByIndex(0), uno::UNO_QUERY );
if ( xMembSupp.is() )
{
xMembers.set(xMembSupp->getMembers());
bRet = sal_True;
}
}
}
}
}
}
return bRet;
}
//------------------------------------------------------------------------
// convert old pivot tables into new datapilot tables
String lcl_GetDimName( const uno::Reference<sheet::XDimensionsSupplier>& xSource, long nDim )
{
rtl::OUString aName;
if ( xSource.is() )
{
uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName );
long nDimCount = xDims->getCount();
if ( nDim < nDimCount )
{
uno::Reference<uno::XInterface> xIntDim =
ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) );
uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY );
if (xDimName.is())
{
try
{
aName = xDimName->getName();
}
catch(uno::Exception&)
{
}
}
}
}
return aName;
}
// static
void ScDPObject::ConvertOrientation( ScDPSaveData& rSaveData,
const ScPivotFieldVector& rFields, sal_uInt16 nOrient,
ScDocument* pDoc, SCROW nRow, SCTAB nTab,
const uno::Reference<sheet::XDimensionsSupplier>& xSource,
bool bOldDefaults,
const ScPivotFieldVector* pRefColFields,
const ScPivotFieldVector* pRefRowFields,
const ScPivotFieldVector* pRefPageFields )
{
// pDoc or xSource must be set
DBG_ASSERT( pDoc || xSource.is(), "missing string source" );
String aDocStr;
ScDPSaveDimension* pDim;
for (ScPivotFieldVector::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt)
{
SCCOL nCol = aIt->nCol;
sal_uInt16 nFuncs = aIt->nFuncMask;
const sheet::DataPilotFieldReference& rFieldRef = aIt->maFieldRef;
if ( nCol == PIVOT_DATA_FIELD )
pDim = rSaveData.GetDataLayoutDimension();
else
{
if ( pDoc )
pDoc->GetString( nCol, nRow, nTab, aDocStr );
else
aDocStr = lcl_GetDimName( xSource, nCol ); // cols must start at 0
if ( aDocStr.Len() )
pDim = rSaveData.GetDimensionByName(aDocStr);
else
pDim = NULL;
}
if ( pDim )
{
if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) // set summary function
{
// generate an individual entry for each function
bool bFirst = true;
// if a dimension is used for column/row/page and data,
// use duplicated dimensions for all data occurrences
if (pRefColFields)
for (ScPivotFieldVector::const_iterator aRefIt = pRefColFields->begin(), aRefEnd = pRefColFields->end(); bFirst && (aRefIt != aRefEnd); ++aRefIt)
if (aRefIt->nCol == nCol)
bFirst = false;
if (pRefRowFields)
for (ScPivotFieldVector::const_iterator aRefIt = pRefRowFields->begin(), aRefEnd = pRefRowFields->end(); bFirst && (aRefIt != aRefEnd); ++aRefIt)
if (aRefIt->nCol == nCol)
bFirst = false;
if (pRefPageFields)
for (ScPivotFieldVector::const_iterator aRefIt = pRefPageFields->begin(), aRefEnd = pRefPageFields->end(); bFirst && (aRefIt != aRefEnd); ++aRefIt)
if (aRefIt->nCol == nCol)
bFirst = false;
// if set via api, a data column may occur several times
// (if the function hasn't been changed yet) -> also look for duplicate data column
for (ScPivotFieldVector::const_iterator aRefIt = rFields.begin(); bFirst && (aRefIt != aIt); ++aRefIt)
if (aRefIt->nCol == nCol)
bFirst = false;
sal_uInt16 nMask = 1;
for (sal_uInt16 nBit=0; nBit<16; nBit++)
{
if ( nFuncs & nMask )
{
sheet::GeneralFunction eFunc = ScDataPilotConversion::FirstFunc( nMask );
ScDPSaveDimension* pCurrDim = bFirst ? pDim : rSaveData.DuplicateDimension(pDim->GetName());
pCurrDim->SetOrientation( nOrient );
pCurrDim->SetFunction( sal::static_int_cast<sal_uInt16>(eFunc) );
if( rFieldRef.ReferenceType == sheet::DataPilotFieldReferenceType::NONE )
pCurrDim->SetReferenceValue( 0 );
else
pCurrDim->SetReferenceValue( &rFieldRef );
bFirst = false;
}
nMask *= 2;
}
}
else // set SubTotals
{
pDim->SetOrientation( nOrient );
sal_uInt16 nFuncArray[16];
sal_uInt16 nFuncCount = 0;
sal_uInt16 nMask = 1;
for (sal_uInt16 nBit=0; nBit<16; nBit++)
{
if ( nFuncs & nMask )
nFuncArray[nFuncCount++] = sal::static_int_cast<sal_uInt16>(ScDataPilotConversion::FirstFunc( nMask ));
nMask *= 2;
}
pDim->SetSubTotals( nFuncCount, nFuncArray );
// ShowEmpty was implicit in old tables,
// must be set for data layout dimension (not accessible in dialog)
if ( bOldDefaults || nCol == PIVOT_DATA_FIELD )
pDim->SetShowEmpty( sal_True );
}
}
}
}
// static
bool ScDPObject::IsOrientationAllowed( sal_uInt16 nOrient, sal_Int32 nDimFlags )
{
bool bAllowed = true;
switch (nOrient)
{
case sheet::DataPilotFieldOrientation_PAGE:
bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_PAGE_ORIENTATION ) == 0;
break;
case sheet::DataPilotFieldOrientation_COLUMN:
bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_COLUMN_ORIENTATION ) == 0;
break;
case sheet::DataPilotFieldOrientation_ROW:
bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_ROW_ORIENTATION ) == 0;
break;
case sheet::DataPilotFieldOrientation_DATA:
bAllowed = ( nDimFlags & sheet::DimensionFlags::NO_DATA_ORIENTATION ) == 0;
break;
default:
{
// allowed to remove from previous orientation
}
}
return bAllowed;
}
// -----------------------------------------------------------------------
// static
sal_Bool ScDPObject::HasRegisteredSources()
{
sal_Bool bFound = sal_False;
uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
if ( xEnAc.is() )
{
uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) );
if ( xEnum.is() && xEnum->hasMoreElements() )
bFound = sal_True;
}
return bFound;
}
// static
uno::Sequence<rtl::OUString> ScDPObject::GetRegisteredSources()
{
long nCount = 0;
uno::Sequence<rtl::OUString> aSeq(0);
// use implementation names...
uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
if ( xEnAc.is() )
{
uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) );
if ( xEnum.is() )
{
while ( xEnum->hasMoreElements() )
{
uno::Any aAddInAny = xEnum->nextElement();
// if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE )
{
uno::Reference<uno::XInterface> xIntFac;
aAddInAny >>= xIntFac;
if ( xIntFac.is() )
{
uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY );
if ( xInfo.is() )
{
rtl::OUString sName = xInfo->getImplementationName();
aSeq.realloc( nCount+1 );
aSeq.getArray()[nCount] = sName;
++nCount;
}
}
}
}
}
}
return aSeq;
}
// use getContext from addincol.cxx
uno::Reference<uno::XComponentContext> getContext(uno::Reference<lang::XMultiServiceFactory> xMSF);
// static
uno::Reference<sheet::XDimensionsSupplier> ScDPObject::CreateSource( const ScDPServiceDesc& rDesc )
{
rtl::OUString aImplName = rDesc.aServiceName;
uno::Reference<sheet::XDimensionsSupplier> xRet = NULL;
uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
if ( xEnAc.is() )
{
uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration(
rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) );
if ( xEnum.is() )
{
while ( xEnum->hasMoreElements() && !xRet.is() )
{
uno::Any aAddInAny = xEnum->nextElement();
// if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE )
{
uno::Reference<uno::XInterface> xIntFac;
aAddInAny >>= xIntFac;
if ( xIntFac.is() )
{
uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY );
if ( xInfo.is() && xInfo->getImplementationName() == aImplName )
{
try
{
// #i113160# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
// passing the context to the component (see ScUnoAddInCollection::Initialize)
uno::Reference<uno::XInterface> xInterface;
uno::Reference<uno::XComponentContext> xCtx = getContext(xManager);
uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
if (xCtx.is() && xCFac.is())
xInterface = xCFac->createInstanceWithContext(xCtx);
if (!xInterface.is())
{
uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
if ( xFac.is() )
xInterface = xFac->createInstance();
}
uno::Reference<lang::XInitialization> xInit( xInterface, uno::UNO_QUERY );
if (xInit.is())
{
// initialize
uno::Sequence<uno::Any> aSeq(4);
uno::Any* pArray = aSeq.getArray();
pArray[0] <<= rtl::OUString( rDesc.aParSource );
pArray[1] <<= rtl::OUString( rDesc.aParName );
pArray[2] <<= rtl::OUString( rDesc.aParUser );
pArray[3] <<= rtl::OUString( rDesc.aParPass );
xInit->initialize( aSeq );
}
xRet = uno::Reference<sheet::XDimensionsSupplier>( xInterface, uno::UNO_QUERY );
}
catch(uno::Exception&)
{
}
}
}
}
}
}
}
return xRet;
}
// ----------------------------------------------------------------------------
ScDPCollection::ScDPCollection(ScDocument* pDocument) :
pDoc( pDocument )
{
}
ScDPCollection::ScDPCollection(const ScDPCollection& r) :
ScCollection(r),
pDoc(r.pDoc)
{
}
ScDPCollection::~ScDPCollection()
{
}
ScDataObject* ScDPCollection::Clone() const
{
return new ScDPCollection(*this);
}
void ScDPCollection::DeleteOnTab( SCTAB nTab )
{
sal_uInt16 nPos = 0;
while ( nPos < nCount )
{
// look for output positions on the deleted sheet
if ( static_cast<const ScDPObject*>(At(nPos))->GetOutRange().aStart.Tab() == nTab )
AtFree(nPos);
else
++nPos;
}
}
void ScDPCollection::UpdateReference( UpdateRefMode eUpdateRefMode,
const ScRange& r, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
for (sal_uInt16 i=0; i<nCount; i++)
((ScDPObject*)At(i))->UpdateReference( eUpdateRefMode, r, nDx, nDy, nDz );
}
sal_Bool ScDPCollection::RefsEqual( const ScDPCollection& r ) const
{
if ( nCount != r.nCount )
return sal_False;
for (sal_uInt16 i=0; i<nCount; i++)
if ( ! ((const ScDPObject*)At(i))->RefsEqual( *((const ScDPObject*)r.At(i)) ) )
return sal_False;
return sal_True; // all equal
}
void ScDPCollection::WriteRefsTo( ScDPCollection& r ) const
{
if ( nCount == r.nCount )
{
//! assert equal names?
for (sal_uInt16 i=0; i<nCount; i++)
((const ScDPObject*)At(i))->WriteRefsTo( *((ScDPObject*)r.At(i)) );
}
else
{
// #i8180# If data pilot tables were deleted with their sheet,
// this collection contains extra entries that must be restored.
// Matching objects are found by their names.
DBG_ASSERT( nCount >= r.nCount, "WriteRefsTo: missing entries in document" );
for (sal_uInt16 nSourcePos=0; nSourcePos<nCount; nSourcePos++)
{
const ScDPObject* pSourceObj = static_cast<const ScDPObject*>(At(nSourcePos));
String aName = pSourceObj->GetName();
bool bFound = false;
for (sal_uInt16 nDestPos=0; nDestPos<r.nCount && !bFound; nDestPos++)
{
ScDPObject* pDestObj = static_cast<ScDPObject*>(r.At(nDestPos));
if ( pDestObj->GetName() == aName )
{
pSourceObj->WriteRefsTo( *pDestObj ); // found object, copy refs
bFound = true;
}
}
if ( !bFound )
{
// none found, re-insert deleted object (see ScUndoDataPilot::Undo)
ScDPObject* pDestObj = new ScDPObject( *pSourceObj );
pDestObj->SetAlive(sal_True);
if ( !r.InsertNewTable(pDestObj) )
{
DBG_ERROR("cannot insert DPObject");
DELETEZ( pDestObj );
}
}
}
DBG_ASSERT( nCount == r.nCount, "WriteRefsTo: couldn't restore all entries" );
}
}
ScDPObject* ScDPCollection::GetByName(const String& rName) const
{
for (sal_uInt16 i=0; i<nCount; i++)
if (static_cast<const ScDPObject*>(pItems[i])->GetName() == rName)
return static_cast<ScDPObject*>(pItems[i]);
return NULL;
}
String ScDPCollection::CreateNewName( sal_uInt16 nMin ) const
{
String aBase( RTL_CONSTASCII_USTRINGPARAM( "Pivot" ) );
//! from Resource?
for (sal_uInt16 nAdd=0; nAdd<=nCount; nAdd++) // nCount+1 tries
{
String aNewName = aBase;
aNewName += String::CreateFromInt32( nMin + nAdd );
sal_Bool bFound = sal_False;
for (sal_uInt16 i=0; i<nCount && !bFound; i++)
if (((const ScDPObject*)pItems[i])->GetName() == aNewName)
bFound = sal_True;
if (!bFound)
return aNewName; // found unused Name
}
return String(); // should not happen
}
// Wang Xu Ming -- 2009-8-17
// DataPilot Migration - Cache&&Performance
long ScDPObject::GetCacheId() const
{
if ( GetSaveData() )
return GetSaveData()->GetCacheId();
else
return mnCacheId;
}
sal_uLong ScDPObject::RefreshCache()
{
if ( pServDesc )
{
// cache table isn't used for external service - do nothing, no error
return 0;
}
CreateObjects();
sal_uLong nErrId = 0;
if ( pSheetDesc)
nErrId = pSheetDesc->CheckValidate( pDoc );
if ( nErrId == 0 )
{
long nOldId = GetCacheId();
long nNewId = pDoc->GetNewDPObjectCacheId();
if ( nOldId >= 0 )
pDoc->RemoveDPObjectCache( nOldId );
ScDPTableDataCache* pCache = NULL;
if ( pSheetDesc )
pCache = pSheetDesc->CreateCache( pDoc, nNewId );
else if ( pImpDesc )
pCache = pImpDesc->CreateCache( pDoc, nNewId );
if ( pCache == NULL )
{
//cache failed
DBG_ASSERT( pCache , " pCache == NULL" );
return STR_ERR_DATAPILOTSOURCE;
}
nNewId = pCache->GetId();
bRefresh = sal_True;
ScDPCollection* pDPCollection = pDoc->GetDPCollection();
sal_uInt16 nCount = pDPCollection->GetCount();
for (sal_uInt16 i=0; i<nCount; i++)
{ //set new cache id
if ( (*pDPCollection)[i]->GetCacheId() == nOldId )
{
(*pDPCollection)[i]->SetCacheId( nNewId );
(*pDPCollection)[i]->SetRefresh();
}
}
DBG_ASSERT( GetCacheId() >= 0, " GetCacheId() >= 0 " );
}
return nErrId;
}
void ScDPObject::SetCacheId( long nCacheId )
{
if ( GetCacheId() != nCacheId )
{
InvalidateSource();
if ( GetSaveData() )
GetSaveData()->SetCacheId( nCacheId );
mnCacheId = nCacheId;
}
}
const ScDPTableDataCache* ScDPObject::GetCache() const
{
return pDoc->GetDPObjectCache( GetCacheId() );
}
// End Comments
void ScDPCollection::FreeTable(ScDPObject* pDPObj)
{
const ScRange& rOutRange = pDPObj->GetOutRange();
const ScAddress& s = rOutRange.aStart;
const ScAddress& e = rOutRange.aEnd;
pDoc->RemoveFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), SC_MF_DP_TABLE);
Free(pDPObj);
}
bool ScDPCollection::InsertNewTable(ScDPObject* pDPObj)
{
bool bSuccess = Insert(pDPObj);
if (bSuccess)
{
const ScRange& rOutRange = pDPObj->GetOutRange();
const ScAddress& s = rOutRange.aStart;
const ScAddress& e = rOutRange.aEnd;
pDoc->ApplyFlagsTab(s.Col(), s.Row(), e.Col(), e.Row(), s.Tab(), SC_MF_DP_TABLE);
}
return bSuccess;
}
bool ScDPCollection::HasDPTable(SCCOL nCol, SCROW nRow, SCTAB nTab) const
{
const ScMergeFlagAttr* pMergeAttr = static_cast<const ScMergeFlagAttr*>(
pDoc->GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG));
if (!pMergeAttr)
return false;
return pMergeAttr->HasDPTable();
}