/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"




// INCLUDE ---------------------------------------------------------------

#include <algorithm>
#include <vector>
#include <set>
#include <hash_map>
#include <hash_set>

#include <tools/debug.hxx>
#include <rtl/math.hxx>
#include <svl/itemprop.hxx>
#include <svl/intitem.hxx>

#include "scitems.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "patattr.hxx"
#include "cell.hxx"

#include "dptabsrc.hxx"
#include "dptabres.hxx"
#include "dptabdat.hxx"
#include "global.hxx"
#include "collect.hxx"
#include "datauno.hxx"		// ScDataUnoConversion
#include "unoguard.hxx"
#include "miscuno.hxx"
#include "unonames.hxx"

#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
#include <com/sun/star/table/CellAddress.hpp>

#include <unotools/collatorwrapper.hxx>
#include <unotools/calendarwrapper.hxx>
#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>

using namespace com::sun::star;
using ::std::vector;
using ::std::set;
using ::std::hash_map;
using ::std::hash_set;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Any;
using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
using ::rtl::OUString;

// -----------------------------------------------------------------------

#define SC_MINCOUNT_LIMIT	1000000

// -----------------------------------------------------------------------

SC_SIMPLE_SERVICE_INFO( ScDPSource,      "ScDPSource",      "com.sun.star.sheet.DataPilotSource" )
SC_SIMPLE_SERVICE_INFO( ScDPDimensions,  "ScDPDimensions",  "com.sun.star.sheet.DataPilotSourceDimensions" )
SC_SIMPLE_SERVICE_INFO( ScDPDimension,   "ScDPDimension",   "com.sun.star.sheet.DataPilotSourceDimension" )
SC_SIMPLE_SERVICE_INFO( ScDPHierarchies, "ScDPHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" )
SC_SIMPLE_SERVICE_INFO( ScDPHierarchy,   "ScDPHierarchy",   "com.sun.star.sheet.DataPilotSourceHierarcy" )
SC_SIMPLE_SERVICE_INFO( ScDPLevels,      "ScDPLevels",      "com.sun.star.sheet.DataPilotSourceLevels" )
SC_SIMPLE_SERVICE_INFO( ScDPLevel,       "ScDPLevel",       "com.sun.star.sheet.DataPilotSourceLevel" )
SC_SIMPLE_SERVICE_INFO( ScDPMembers,     "ScDPMembers",     "com.sun.star.sheet.DataPilotSourceMembers" )
SC_SIMPLE_SERVICE_INFO( ScDPMember,      "ScDPMember",      "com.sun.star.sheet.DataPilotSourceMember" )

// -----------------------------------------------------------------------

// property maps for PropertySetInfo
//	DataDescription / NumberFormat are internal

// -----------------------------------------------------------------------

//!	move to a header?
sal_Bool lcl_GetBoolFromAny( const uno::Any& aAny )
{
	if ( aAny.getValueTypeClass() == uno::TypeClass_BOOLEAN )
		return *(sal_Bool*)aAny.getValue();
	return sal_False;
}

void lcl_SetBoolInAny( uno::Any& rAny, sal_Bool bValue )
{
	rAny.setValue( &bValue, getBooleanCppuType() );
}

// -----------------------------------------------------------------------

ScDPSource::ScDPSource( ScDPTableData* pD ) :
	pData( pD ),
	pDimensions( NULL ),
	nColDimCount( 0 ),
	nRowDimCount( 0 ),
	nDataDimCount( 0 ),
	nPageDimCount( 0 ),
	bColumnGrand( sal_True ),		// default is true
	bRowGrand( sal_True ),
	bIgnoreEmptyRows( sal_False ),
	bRepeatIfEmpty( sal_False ),
	nDupCount( 0 ),
	pResData( NULL ),
	pColResRoot( NULL ),
	pRowResRoot( NULL ),
	pColResults( NULL ),
	pRowResults( NULL ),
	bResultOverflow( sal_False ),
    mpGrandTotalName(NULL)
{
	pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
}

ScDPSource::~ScDPSource()
{
	if (pDimensions)
		pDimensions->release();		// ref-counted

	//!	free lists

	delete[] pColResults;
	delete[] pRowResults;

	delete pColResRoot;
	delete pRowResRoot;
	delete pResData;
}

void ScDPSource::SetGrandTotalName(const ::rtl::OUString& rName)
{
    mpGrandTotalName.reset(new ::rtl::OUString(rName));
}

const ::rtl::OUString* ScDPSource::GetGrandTotalName() const
{
    return mpGrandTotalName.get();
}

sal_uInt16 ScDPSource::GetOrientation(long nColumn)
{
	long i;
	for (i=0; i<nColDimCount; i++)
		if (nColDims[i] == nColumn)
			return sheet::DataPilotFieldOrientation_COLUMN;
	for (i=0; i<nRowDimCount; i++)
		if (nRowDims[i] == nColumn)
			return sheet::DataPilotFieldOrientation_ROW;
	for (i=0; i<nDataDimCount; i++)
		if (nDataDims[i] == nColumn)
			return sheet::DataPilotFieldOrientation_DATA;
	for (i=0; i<nPageDimCount; i++)
		if (nPageDims[i] == nColumn)
			return sheet::DataPilotFieldOrientation_PAGE;
	return sheet::DataPilotFieldOrientation_HIDDEN;
}

long ScDPSource::GetDataDimensionCount()
{
	return nDataDimCount;
}

ScDPDimension* ScDPSource::GetDataDimension(long nIndex)
{
    if (nIndex < 0 || nIndex >= nDataDimCount)
        return NULL;

    long nDimIndex = nDataDims[nIndex];
    return GetDimensionsObject()->getByIndex(nDimIndex);
}

String ScDPSource::GetDataDimName( long nIndex )
{
	String aRet;
    ScDPDimension* pDim = GetDataDimension(nIndex);
    if (pDim)
        aRet = String(pDim->getName());
	return aRet;
}

long ScDPSource::GetPosition(long nColumn)
{
	long i;
	for (i=0; i<nColDimCount; i++)
		if (nColDims[i] == nColumn)
			return i;
	for (i=0; i<nRowDimCount; i++)
		if (nRowDims[i] == nColumn)
			return i;
	for (i=0; i<nDataDimCount; i++)
		if (nDataDims[i] == nColumn)
			return i;
	for (i=0; i<nPageDimCount; i++)
		if (nPageDims[i] == nColumn)
			return i;
	return 0;
}

sal_Bool lcl_TestSubTotal( sal_Bool& rAllowed, long nColumn, long* pArray, long nCount, ScDPSource* pSource )
{
	for (long i=0; i<nCount; i++)
		if (pArray[i] == nColumn)
		{
			//	no subtotals for data layout dim, no matter where
			if ( pSource->IsDataLayoutDimension(nColumn) )
				rAllowed = sal_False;
			else
			{
				//	no subtotals if no other dim but data layout follows
				long nNextIndex = i+1;
				if ( nNextIndex < nCount && pSource->IsDataLayoutDimension(pArray[nNextIndex]) )
					++nNextIndex;
				if ( nNextIndex >= nCount )
					rAllowed = sal_False;
			}

			return sal_True;	// found
		}
	return sal_False;
}

sal_Bool ScDPSource::SubTotalAllowed(long nColumn)
{
	//!	cache this at ScDPResultData
	sal_Bool bAllowed = sal_True;
	if ( lcl_TestSubTotal( bAllowed, nColumn, nColDims, nColDimCount, this ) )
		return bAllowed;
	if ( lcl_TestSubTotal( bAllowed, nColumn, nRowDims, nRowDimCount, this ) )
		return bAllowed;
	return bAllowed;
}

void lcl_RemoveDim( long nRemove, long* pDims, long& rCount )
{
	for (long i=0; i<rCount; i++)
		if ( pDims[i] == nRemove )
		{
			for (long j=i; j+1<rCount; j++)
				pDims[j] = pDims[j+1];
			--rCount;
			return;
		}
}

void ScDPSource::SetOrientation(long nColumn, sal_uInt16 nNew)
{
	//!	change to no-op if new orientation is equal to old?

	// remove from old list
	lcl_RemoveDim( nColumn, nColDims, nColDimCount );
	lcl_RemoveDim( nColumn, nRowDims, nRowDimCount );
	lcl_RemoveDim( nColumn, nDataDims, nDataDimCount );
	lcl_RemoveDim( nColumn, nPageDims, nPageDimCount );

	// add to new list
	switch (nNew)
	{
		case sheet::DataPilotFieldOrientation_COLUMN:
			nColDims[nColDimCount++] = nColumn;
			break;
		case sheet::DataPilotFieldOrientation_ROW:
			nRowDims[nRowDimCount++] = nColumn;
			break;
		case sheet::DataPilotFieldOrientation_DATA:
			nDataDims[nDataDimCount++] = nColumn;
			break;
		case sheet::DataPilotFieldOrientation_PAGE:
			nPageDims[nPageDimCount++] = nColumn;
			break;
            // Wang Xu Ming -- 2009-9-1
            // DataPilot Migration - Cache&&Performance
        case sheet::DataPilotFieldOrientation_HIDDEN:
            break;
            // End Comments
		default: 
			DBG_ERROR( "ScDPSource::SetOrientation: unexpected orientation" );
			break;
	}
}

sal_Bool ScDPSource::IsDataLayoutDimension(long nDim)
{
	return nDim == pData->GetColumnCount();
}

sal_uInt16 ScDPSource::GetDataLayoutOrientation()
{
	return GetOrientation(pData->GetColumnCount());
}

sal_Bool ScDPSource::IsDateDimension(long nDim)
{
	return pData->IsDateDimension(nDim);
}

sal_uInt32	ScDPSource::GetNumberFormat(long nDim)
{
	return pData->GetNumberFormat( nDim );
}

ScDPDimensions* ScDPSource::GetDimensionsObject()
{
	if (!pDimensions)
	{
		pDimensions = new ScDPDimensions(this);
		pDimensions->acquire();						// ref-counted
	}
	return pDimensions;
}

uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions() throw(uno::RuntimeException)
{
	return GetDimensionsObject();
}

void ScDPSource::SetDupCount( long nNew )
{
	nDupCount = nNew;
}

ScDPDimension* ScDPSource::AddDuplicated(long /* nSource */, const String& rNewName)
{
	DBG_ASSERT( pDimensions, "AddDuplicated without dimensions?" );

	//	re-use

	long nOldDimCount = pDimensions->getCount();
	for (long i=0; i<nOldDimCount; i++)
	{
		ScDPDimension* pDim = pDimensions->getByIndex(i);
		if (pDim && String(pDim->getName()) == rNewName)
		{
			//!	test if pDim is a duplicate of source
			return pDim;
		}
	}

	SetDupCount( nDupCount + 1 );
	pDimensions->CountChanged();		// uses nDupCount

	return pDimensions->getByIndex( pDimensions->getCount() - 1 );
}

long ScDPSource::GetSourceDim(long nDim)
{
	//	original source dimension or data layout dimension?
	if ( nDim <= pData->GetColumnCount() )
		return nDim;

	if ( nDim < pDimensions->getCount() )
	{
		ScDPDimension* pDimObj = pDimensions->getByIndex( nDim );
		if ( pDimObj )
		{
			long nSource = pDimObj->GetSourceDim();
			if ( nSource >= 0 )
				return nSource;
		}
	}

	DBG_ERROR("GetSourceDim: wrong dim");
	return nDim;
}

uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
															throw(uno::RuntimeException)
{
	CreateRes_Impl();		// create pColResRoot and pRowResRoot

	if ( bResultOverflow )		// set in CreateRes_Impl
	{
		//	no results available
		throw uno::RuntimeException();
	}

	long nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure());
	long nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure());

	//	allocate full sequence
	//!	leave out empty rows???

	uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
	uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
	for (long nRow = 0; nRow < nRowCount; nRow++)
	{
		uno::Sequence<sheet::DataResult> aColSeq( nColCount );
		//	use default values of DataResult
		pRowAry[nRow] = aColSeq;
	}

	long nSeqRow = 0;
	pRowResRoot->FillDataResults( pColResRoot, aSeq, nSeqRow, pResData->GetRowStartMeasure() );

	return aSeq;
}

void SAL_CALL ScDPSource::refresh() throw(uno::RuntimeException)
{
	disposeData();
}

void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
												throw(uno::RuntimeException)
{
	DBG_ERROR("not implemented");	//! exception?
}

void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
												throw(uno::RuntimeException)
{
	DBG_ERROR("not implemented");	//! exception?
}

Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
    throw (uno::RuntimeException)
{
    long nColumnCount = GetData()->GetColumnCount();

    typedef hash_map<String, long, ScStringHashCode> FieldNameMapType;
    FieldNameMapType aFieldNames;
    for (long i = 0; i < nColumnCount; ++i)
    {
        aFieldNames.insert(
            FieldNameMapType::value_type(GetData()->getDimensionName(i), i));
    }

    // collect ScDPItemData for each filtered column
    vector<ScDPCacheTable::Criterion> aFilterCriteria;
    sal_Int32 nFilterCount = aFilters.getLength();
    for (sal_Int32 i = 0; i < nFilterCount; ++i)
    {
        const sheet::DataPilotFieldFilter& rFilter = aFilters[i];
        String aFieldName( rFilter.FieldName );
        for (long nCol = 0; nCol < nColumnCount; ++nCol)
        {
            if ( aFieldName == pData->getDimensionName(nCol) )
            {
                ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
                ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
                                        GetLevelsObject()->getByIndex(0)->GetMembersObject();
                sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValue );
                if ( nIndex >= 0 )
                {
                    ScDPItemData aItem;
                    pMembers->getByIndex(nIndex)->FillItemData( aItem );
                    aFilterCriteria.push_back( ScDPCacheTable::Criterion() );
                    aFilterCriteria.back().mnFieldIndex = nCol;
                    aFilterCriteria.back().mpFilter.reset(
                        new ScDPCacheTable::SingleFilter(aItem.GetString()/*rSharedString, nMatchStrId*/, aItem.GetValue(), aItem.IsValue()) );
                }
            }
        }
    }

    // Take into account the visibilities of field members.
    ScDPResultVisibilityData aResVisData(/*rSharedString, */this);
    pRowResRoot->FillVisibilityData(aResVisData);
    pColResRoot->FillVisibilityData(aResVisData);
    aResVisData.fillFieldFilters(aFilterCriteria);

    Sequence< Sequence<Any> > aTabData;
    hash_set<sal_Int32> aCatDims;
    GetCategoryDimensionIndices(aCatDims);
    pData->GetDrillDownData(aFilterCriteria, aCatDims, aTabData);
    return aTabData;
}

String ScDPSource::getDataDescription()
{
	CreateRes_Impl();		// create pResData

	String aRet;
	if ( pResData->GetMeasureCount() == 1 )
    {
        bool bTotalResult = false;
        aRet = pResData->GetMeasureString( 0, sal_True, SUBTOTAL_FUNC_NONE, bTotalResult );
    }

	//	empty for more than one measure

	return aRet;
}

sal_Bool ScDPSource::getColumnGrand() const
{
	return bColumnGrand;
}

void ScDPSource::setColumnGrand(sal_Bool bSet)
{
	bColumnGrand = bSet;
}

sal_Bool ScDPSource::getRowGrand() const
{
	return bRowGrand;
}

void ScDPSource::setRowGrand(sal_Bool bSet)
{
	bRowGrand = bSet;
}

sal_Bool ScDPSource::getIgnoreEmptyRows() const
{
	return bIgnoreEmptyRows;
}

void ScDPSource::setIgnoreEmptyRows(sal_Bool bSet)
{
	bIgnoreEmptyRows = bSet;
	pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
}

sal_Bool ScDPSource::getRepeatIfEmpty() const
{
	return bRepeatIfEmpty;
}

void ScDPSource::setRepeatIfEmpty(sal_Bool bSet)
{
	bRepeatIfEmpty = bSet;
	pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
}

void ScDPSource::validate()		//! ???
{
	CreateRes_Impl();
}

void ScDPSource::disposeData()
{
	if ( pResData )
	{
		//	reset all data...

		DELETEZ(pColResRoot);
		DELETEZ(pRowResRoot);
		DELETEZ(pResData);
		delete[] pColResults;
		delete[] pRowResults;
		pColResults = NULL;
		pRowResults = NULL;
		aColLevelList.Clear();
		aRowLevelList.Clear();
	}

	if ( pDimensions )
	{
		pDimensions->release();	// ref-counted
		pDimensions = NULL;		//	settings have to be applied (from SaveData) again!
	}
	SetDupCount( 0 );

	//!	Test ????
	nColDimCount = nRowDimCount = nDataDimCount = nPageDimCount = 0;

	pData->DisposeData();	// cached entries etc.
	bResultOverflow = sal_False;
}

long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, long nLevels )
{
    //	Calculate the product of the member count for those consecutive levels that
    //	have the "show all" flag, one following level, and the data layout dimension.

    long nTotal = 1;
    long nDataCount = 1;
    sal_Bool bWasShowAll = sal_True;
    long nPos = nLevels;
    while ( nPos > 0 )
    {
        --nPos;

        if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
        {
            DBG_ERROR("lcl_CountMinMembers: multiple levels from one dimension not implemented");
            return 0;
        }

        sal_Bool bDo = sal_False;
        if ( ppDim[nPos]->getIsDataLayoutDimension() )
        {
            //	data layout dim doesn't interfere with "show all" flags
            nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
            if ( nDataCount == 0 )
                nDataCount = 1;
        }
        else if ( bWasShowAll )     // "show all" set for all following levels?
        {
            bDo = sal_True;
            if ( !ppLevel[nPos]->getShowEmpty() )
            {
                //	this level is counted, following ones are not
                bWasShowAll = sal_False;
            }
        }
        if ( bDo )
        {
            long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
            if ( nThisCount == 0 )
            {
                nTotal = 1;         //	empty level -> start counting from here
                                    //!	start with visible elements in this level?
            }
            else
            {
                if ( nTotal >= LONG_MAX / nThisCount )
                    return LONG_MAX;                        //	overflow
                nTotal *= nThisCount;
            }
        }
    }

    //	always include data layout dim, even after restarting
    if ( nTotal >= LONG_MAX / nDataCount )
        return LONG_MAX;                        //	overflow
    nTotal *= nDataCount;

    return nTotal;
}

long lcl_GetIndexFromName( const rtl::OUString rName, const uno::Sequence<rtl::OUString>& rElements )
{
    long nCount = rElements.getLength();
    const rtl::OUString* pArray = rElements.getConstArray();
    for (long nPos=0; nPos<nCount; nPos++)
        if (pArray[nPos] == rName)
            return nPos;

    return -1;  // not found
}

void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
{
    long* nDims = bIsRow ? nRowDims : nColDims;
    long nDimCount = bIsRow ? nRowDimCount : nColDimCount;

    for (long i = 0; i < nDimCount; ++i)
    {
        ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nDims[i] );
        long nHierarchy = pDim->getUsedHierarchy();
        if ( nHierarchy >= pDim->GetHierarchiesObject()->getCount() )
            nHierarchy = 0;
        ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
        long nCount = pLevels->getCount();

        //!	Test
        if ( pDim->getIsDataLayoutDimension() && nDataDimCount < 2 )
            nCount = 0;
        //!	Test

        for (long j = 0; j < nCount; ++j)
        {
            ScDPLevel* pLevel = pLevels->getByIndex(j);
            pLevel->EvaluateSortOrder();

            // no layout flags for column fields, only for row fields
            pLevel->SetEnableLayout( bIsRow );           

            if ( pLevel->GetAutoShow().IsEnabled )
                rHasAutoShow = sal_True;

            if (bIsRow)
            {
                rInfo.aRowLevelDims.push_back(nDims[i]);
                rInfo.aRowDims.push_back(pDim);
                rInfo.aRowLevels.push_back(pLevel);
            }
            else
            {
                rInfo.aColLevelDims.push_back(nDims[i]);
                rInfo.aColDims.push_back(pDim);
                rInfo.aColLevels.push_back(pLevel);
            }

            pLevel->GetMembersObject();                 // initialize for groups
        }
    }
}

void ScDPSource::GetCategoryDimensionIndices(hash_set<sal_Int32>& rCatDims)
{
    hash_set<sal_Int32> aCatDims;
    for (long i = 0; i < nColDimCount; ++i)
    {
        sal_Int32 nDim = static_cast<sal_Int32>(nColDims[i]);
        if (!IsDataLayoutDimension(nDim))
            aCatDims.insert(nDim);
    }

    for (long i = 0; i < nRowDimCount; ++i)
    {
        sal_Int32 nDim = static_cast<sal_Int32>(nRowDims[i]);
        if (!IsDataLayoutDimension(nDim))
            aCatDims.insert(nDim);
    }

    for (long i = 0; i < nPageDimCount; ++i)
    {
        sal_Int32 nDim = static_cast<sal_Int32>(nPageDims[i]);
        if (!IsDataLayoutDimension(nDim))
            aCatDims.insert(nDim);
    }

    rCatDims.swap(aCatDims);
}

void ScDPSource::FilterCacheTableByPageDimensions()
{

    // filter table by page dimensions.
    vector<ScDPCacheTable::Criterion> aCriteria;
    for (long i = 0; i < nPageDimCount; ++i)
    {
        ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nPageDims[i]);
        long nField = pDim->GetDimension();

        ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
            GetLevelsObject()->getByIndex(0)->GetMembersObject();

        long nMemCount = pMems->getCount();
        ScDPCacheTable::Criterion aFilter;
        aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
        aFilter.mpFilter.reset(new ScDPCacheTable::GroupFilter(/*rSharedString*/));
        ScDPCacheTable::GroupFilter* pGrpFilter = 
            static_cast<ScDPCacheTable::GroupFilter*>(aFilter.mpFilter.get());
        for (long j = 0; j < nMemCount; ++j)
        {
            ScDPMember* pMem = pMems->getByIndex(j);
            if (pMem->getIsVisible())
            {
                ScDPItemData aData;
                pMem->FillItemData(aData);
                pGrpFilter->addMatchItem(aData.GetString(), aData.GetValue(), aData.IsValue());
            }
        }
        if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(nMemCount))
            // there is at least one invisible item.  Add this filter criterion to the mix.
            aCriteria.push_back(aFilter);

        if (!pDim || !pDim->HasSelectedPage())
            continue;

        const ScDPItemData& rData = pDim->GetSelectedData();
        aCriteria.push_back(ScDPCacheTable::Criterion());
        ScDPCacheTable::Criterion& r = aCriteria.back();
        r.mnFieldIndex = static_cast<sal_Int32>(nField);
        r.mpFilter.reset(
            new ScDPCacheTable::SingleFilter(rData.GetString()/*rSharedString, nStrId*/, rData.GetValue(), rData.IsValue()));
    }
    if (!aCriteria.empty())
    {
        hash_set<sal_Int32> aCatDims;
        GetCategoryDimensionIndices(aCatDims);
        pData->FilterCacheTable(aCriteria, aCatDims);
    }
}

void ScDPSource::CreateRes_Impl()
{
	if ( !pResData )
	{
		sal_uInt16 nDataOrient = GetDataLayoutOrientation();
        if ( nDataDimCount > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
                                    nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
		{
			//	if more than one data dimension, data layout orientation must be set
			SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW );
			nDataOrient = sheet::DataPilotFieldOrientation_ROW;
		}

        // TODO: Aggreate pDataNames, pDataRefValues, nDataRefOrient, and
        // eDataFunctions into a structure and use vector instead of static
        // or pointer arrays.
		String* pDataNames = NULL;
		sheet::DataPilotFieldReference* pDataRefValues = NULL;
        ScSubTotalFunc eDataFunctions[SC_DAPI_MAXFIELDS];
		sal_uInt16 nDataRefOrient[SC_DAPI_MAXFIELDS];
		if (nDataDimCount)
		{
			pDataNames = new String[nDataDimCount];
			pDataRefValues = new sheet::DataPilotFieldReference[nDataDimCount];
		}

        ScDPTableData::CalcInfo aInfo;


		//	LateInit (initialize only those rows/children that are used) can be used unless
		//	any data dimension needs reference values from column/row dimensions
		sal_Bool bLateInit = sal_True;

        // Go through all data dimensions (i.e. fields) and build their meta data
        // so that they can be passed on to ScDPResultData instance later.
        // TODO: aggregate all of data dimension info into a structure.
		long i;
		for (i=0; i<nDataDimCount; i++)
		{
            // Get function for each data field.
			long nDimIndex = nDataDims[i];
			ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
			sheet::GeneralFunction eUser = (sheet::GeneralFunction)pDim->getFunction();
			if (eUser == sheet::GeneralFunction_AUTO)
			{
				//!	test for numeric data
				eUser = sheet::GeneralFunction_SUM;
			}

            // Map UNO's enum to internal enum ScSubTotalFunc.
			eDataFunctions[i] = ScDataUnoConversion::GeneralToSubTotal( eUser );

            // Get reference field/item information.
			pDataRefValues[i] = pDim->GetReferenceValue();
			nDataRefOrient[i] = sheet::DataPilotFieldOrientation_HIDDEN;	// default if not used
			sal_Int32 eRefType = pDataRefValues[i].ReferenceType;
			if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
				 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
				 eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
				 eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
			{
				long nColumn = lcl_GetIndexFromName( pDataRefValues[i].ReferenceField,
										GetDimensionsObject()->getElementNames() );
				if ( nColumn >= 0 )
				{
					nDataRefOrient[i] = GetOrientation( nColumn );
					//	need fully initialized results to find reference values
					//	(both in column or row dimensions), so updated values or
					//	differences to 0 can be displayed even for empty results.
					bLateInit = sal_False;
				}
			}

			pDataNames[i] = String( pDim->getName() );	//! label?

			//	asterisk is added to duplicated dimension names by ScDPSaveData::WriteToSource
			//!	modify user visible strings as in ScDPResultData::GetMeasureString instead!

			pDataNames[i].EraseTrailingChars('*');

			//!	if the name is overridden by user, a flag must be set
			//!	so the user defined name replaces the function string and field name.

			//!	the complete name (function and field) must be stored at the dimension

			long nSource = ((ScDPDimension*)pDim)->GetSourceDim();
            if (nSource >= 0)
                aInfo.aDataSrcCols.push_back(nSource);
            else
                aInfo.aDataSrcCols.push_back(nDimIndex);
		}

		pResData = new ScDPResultData( this );
		pResData->SetMeasureData( nDataDimCount, eDataFunctions, pDataRefValues, nDataRefOrient, pDataNames );
		pResData->SetDataLayoutOrientation(nDataOrient);
		pResData->SetLateInit( bLateInit );

		delete[] pDataNames;
		delete[] pDataRefValues;

        bool bHasAutoShow = false;

        ScDPInitState aInitState;

        // Page field selections restrict the members shown in related fields
        // (both in column and row fields). aInitState is filled with the page
        // field selections, they are kept across the data iterator loop.

        for (i=0; i<nPageDimCount; i++)
        {
            ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nPageDims[i] );
            if ( pDim->HasSelectedPage() )
	            aInitState.AddMember( nPageDims[i], GetMemberId( nPageDims[i],  pDim->GetSelectedData() ) );
        }

		pColResRoot = new ScDPResultMember( pResData, /*NULL, NULL, NULL, */bColumnGrand );
		pRowResRoot = new ScDPResultMember( pResData, /*NULL, NULL, NULL, */bRowGrand );

        FillCalcInfo(false, aInfo, bHasAutoShow);
        long nColLevelCount = aInfo.aColLevels.size();

        pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
        pColResRoot->SetHasElements();

        FillCalcInfo(true, aInfo, bHasAutoShow);
        long nRowLevelCount = aInfo.aRowLevels.size();

        if ( nRowLevelCount > 0 )
        {
            // disable layout flags for the innermost row field (level)
            aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( sal_False );
        }

        pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
        pRowResRoot->SetHasElements();

        // initialize members object also for all page dimensions (needed for numeric groups)
        for (i=0; i<nPageDimCount; i++)
        {
            ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nPageDims[i] );
            long nHierarchy = pDim->getUsedHierarchy();
            if ( nHierarchy >= pDim->GetHierarchiesObject()->getCount() )
                nHierarchy = 0;

            ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
            long nCount = pLevels->getCount();
            for (long j=0; j<nCount; j++)
                pLevels->getByIndex(j)->GetMembersObject();             // initialize for groups
        }

		//	pre-check: calculate minimum number of result columns / rows from
		//	levels that have the "show all" flag set

		long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
		long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );

		if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
		{
			//	resulting table is too big -> abort before calculating
			//	(this relies on late init, so no members are allocated in InitFrom above)

			bResultOverflow = sal_True;
		}
		else
		{
            FilterCacheTableByPageDimensions();

            aInfo.aPageDims.reserve(nPageDimCount);
            for (i = 0; i < nPageDimCount; ++i)
                aInfo.aPageDims.push_back(nPageDims[i]);

            aInfo.pInitState = &aInitState;
            aInfo.pColRoot   = pColResRoot;
            aInfo.pRowRoot   = pRowResRoot;
            pData->CalcResults(aInfo, false);

			pColResRoot->CheckShowEmpty();
			pRowResRoot->CheckShowEmpty();
			// ----------------------------------------------------------------
			//  With all data processed, calculate the final results:

            //  UpdateDataResults calculates all original results from the collected values,
			//  and stores them as reference values if needed.
			pRowResRoot->UpdateDataResults( pColResRoot, pResData->GetRowStartMeasure() );

			if ( bHasAutoShow )     // do the double calculation only if AutoShow is used
			{
			    //  Find the desired members and set bAutoHidden flag for the others
    			pRowResRoot->DoAutoShow( pColResRoot );

			    //  Reset all results to empty, so they can be built again with data for the
    			//  desired members only.
    			pColResRoot->ResetResults( sal_True );
    			pRowResRoot->ResetResults( sal_True );
                pData->CalcResults(aInfo, true);

    			//  Call UpdateDataResults again, with the new (limited) values.
    			pRowResRoot->UpdateDataResults( pColResRoot, pResData->GetRowStartMeasure() );
			}

			//  SortMembers does the sorting by a result dimension, using the orginal results,
			//  but not running totals etc.
			pRowResRoot->SortMembers( pColResRoot );

			//  UpdateRunningTotals calculates running totals along column/row dimensions,
			//  differences from other members (named or relative), and column/row percentages
			//  or index values.
			//  Running totals and relative differences need to be done using the sorted values.
			//  Column/row percentages and index values must be done after sorting, because the
			//  results may no longer be in the right order (row total for percentage of row is
			//  always 1).
			ScDPRunningTotalState aRunning( pColResRoot, pRowResRoot );
			ScDPRowTotals aTotals;
			pRowResRoot->UpdateRunningTotals( pColResRoot, pResData->GetRowStartMeasure(), aRunning, aTotals );

			// ----------------------------------------------------------------
		}
	}
}

//UNUSED2009-05 void ScDPSource::DumpState( ScDocument* pDoc, const ScAddress& rPos )
//UNUSED2009-05 {
//UNUSED2009-05     CreateRes_Impl();
//UNUSED2009-05 
//UNUSED2009-05     ScAddress aDocPos( rPos );
//UNUSED2009-05 
//UNUSED2009-05     if (pColResRoot->GetChildDimension())
//UNUSED2009-05         pColResRoot->GetChildDimension()->DumpState( NULL, pDoc, aDocPos );
//UNUSED2009-05     pRowResRoot->DumpState( pColResRoot, pDoc, aDocPos );
//UNUSED2009-05 }

void ScDPSource::FillLevelList( sal_uInt16 nOrientation, List& rList )
{
	rList.Clear();

	long nDimCount = 0;
	long* pDimIndex = NULL;
	switch (nOrientation)
	{
		case sheet::DataPilotFieldOrientation_COLUMN:
			pDimIndex = nColDims;
			nDimCount = nColDimCount;
			break;
		case sheet::DataPilotFieldOrientation_ROW:
			pDimIndex = nRowDims;
			nDimCount = nRowDimCount;
			break;
		case sheet::DataPilotFieldOrientation_DATA:
			pDimIndex = nDataDims;
			nDimCount = nDataDimCount;
			break;
		case sheet::DataPilotFieldOrientation_PAGE:
			pDimIndex = nPageDims;
			nDimCount = nPageDimCount;
			break;
		default:
			DBG_ERROR( "ScDPSource::FillLevelList: unexpected orientation" );
			break;
	}
	if (!pDimIndex)
	{
		DBG_ERROR("invalid orientation");
		return;
	}

	ScDPDimensions* pDims = GetDimensionsObject();
	for (long nDim=0; nDim<nDimCount; nDim++)
	{
		ScDPDimension* pDim = pDims->getByIndex(pDimIndex[nDim]);
		DBG_ASSERT( pDim->getOrientation() == nOrientation, "orientations are wrong" );

		ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
		long nHierarchy = pDim->getUsedHierarchy();
		if ( nHierarchy >= pHiers->getCount() )
			nHierarchy = 0;
		ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
		ScDPLevels* pLevels = pHier->GetLevelsObject();
		long nLevCount = pLevels->getCount();
		for (long nLev=0; nLev<nLevCount; nLev++)
		{
			ScDPLevel* pLevel = pLevels->getByIndex(nLev);
			rList.Insert( pLevel, LIST_APPEND );
		}
	}
}

void ScDPSource::FillMemberResults()
{
	if ( !pColResults && !pRowResults )
	{
		CreateRes_Impl();

		if ( bResultOverflow )		// set in CreateRes_Impl
		{
			//	no results available -> abort (leave empty)
			//	exception is thrown in ScDPSource::getResults
			return;
		}

		FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList );
		long nColLevelCount = aColLevelList.Count();
		if (nColLevelCount)
		{
			long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure());
			pColResults = new uno::Sequence<sheet::MemberResult>[nColLevelCount];
			for (long i=0; i<nColLevelCount; i++)
				pColResults[i].realloc(nColDimSize);

			// ScDPResultDimension* pColResDim = pColResRoot->GetChildDimension();
			// pColResDim->FillMemberResults( pColResults, 0, pResData->GetColStartMeasure() );
			long nPos = 0;
			pColResRoot->FillMemberResults( pColResults, nPos, pResData->GetColStartMeasure(),
											sal_True, NULL, NULL );
		}

		FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
		long nRowLevelCount = aRowLevelList.Count();
		if (nRowLevelCount)
		{
			long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
			pRowResults = new uno::Sequence<sheet::MemberResult>[nRowLevelCount];
			for (long i=0; i<nRowLevelCount; i++)
				pRowResults[i].realloc(nRowDimSize);

			// ScDPResultDimension* pRowResDim = pRowResRoot->GetChildDimension();
			// pRowResDim->FillMemberResults( pRowResults, 0, pResData->GetRowStartMeasure() );
			long nPos = 0;
			pRowResRoot->FillMemberResults( pRowResults, nPos, pResData->GetRowStartMeasure(),
											sal_True, NULL, NULL );
		}
	}
}

const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( ScDPLevel* pLevel )
{
	FillMemberResults();

	long i;
	long nColCount = aColLevelList.Count();
	for (i=0; i<nColCount; i++)
	{
		ScDPLevel* pColLevel = (ScDPLevel*)aColLevelList.GetObject(i);
		if ( pColLevel == pLevel )
			return pColResults+i;
	}
	long nRowCount = aRowLevelList.Count();
	for (i=0; i<nRowCount; i++)
	{
		ScDPLevel* pRowLevel = (ScDPLevel*)aRowLevelList.GetObject(i);
		if ( pRowLevel == pLevel )
			return pRowResults+i;
	}
	return NULL;
}

// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
														throw(uno::RuntimeException)
{
	ScUnoGuard aGuard;
    using beans::PropertyAttribute::READONLY;

    static SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
	{
		{MAP_CHAR_LEN(SC_UNO_COLGRAND),	0,	&getBooleanCppuType(),				0, 0 },
		{MAP_CHAR_LEN(SC_UNO_DATADESC),	0,	&getCppuType((rtl::OUString*)0),	beans::PropertyAttribute::READONLY, 0 },
		{MAP_CHAR_LEN(SC_UNO_IGNOREEM),	0,	&getBooleanCppuType(),				0, 0 },		// for sheet data only
		{MAP_CHAR_LEN(SC_UNO_REPEATIF),	0,	&getBooleanCppuType(),				0, 0 },		// for sheet data only
		{MAP_CHAR_LEN(SC_UNO_ROWGRAND),	0,	&getBooleanCppuType(),				0, 0 },
        {MAP_CHAR_LEN(SC_UNO_ROWFIELDCOUNT),    0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
        {MAP_CHAR_LEN(SC_UNO_COLUMNFIELDCOUNT), 0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
        {MAP_CHAR_LEN(SC_UNO_DATAFIELDCOUNT),   0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
        {MAP_CHAR_LEN(SC_UNO_GRANDTOTAL_NAME),  0, &getCppuType(static_cast<OUString*>(0)), 0, 0 },
        {0,0,0,0,0,0}
	};
	static uno::Reference<beans::XPropertySetInfo> aRef =
		new SfxItemPropertySetInfo( aDPSourceMap_Impl );
	return aRef;
}

void SAL_CALL ScDPSource::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue )
				throw(beans::UnknownPropertyException, beans::PropertyVetoException,
						lang::IllegalArgumentException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_COLGRAND ) )
		setColumnGrand( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_ROWGRAND ) )
		setRowGrand( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_IGNOREEM ) )
		setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_REPEATIF ) )
		setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
    else if (aNameStr.EqualsAscii(SC_UNO_GRANDTOTAL_NAME))
    {
        OUString aName;
        if (aValue >>= aName)
            mpGrandTotalName.reset(new OUString(aName));
    }
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
}

uno::Any SAL_CALL ScDPSource::getPropertyValue( const rtl::OUString& aPropertyName )
				throw(beans::UnknownPropertyException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	uno::Any aRet;
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_COLGRAND ) )
		lcl_SetBoolInAny( aRet, getColumnGrand() );
	else if ( aNameStr.EqualsAscii( SC_UNO_ROWGRAND ) )
		lcl_SetBoolInAny( aRet, getRowGrand() );
	else if ( aNameStr.EqualsAscii( SC_UNO_IGNOREEM ) )
		lcl_SetBoolInAny( aRet, getIgnoreEmptyRows() );
	else if ( aNameStr.EqualsAscii( SC_UNO_REPEATIF ) )
		lcl_SetBoolInAny( aRet, getRepeatIfEmpty() );
	else if ( aNameStr.EqualsAscii( SC_UNO_DATADESC ) )				// read-only
		aRet <<= rtl::OUString( getDataDescription() );
    else if ( aNameStr.EqualsAscii( SC_UNO_ROWFIELDCOUNT ) )        // read-only
        aRet <<= static_cast<sal_Int32>(nRowDimCount);
    else if ( aNameStr.EqualsAscii( SC_UNO_COLUMNFIELDCOUNT ) )     // read-only
        aRet <<= static_cast<sal_Int32>(nColDimCount);
    else if ( aNameStr.EqualsAscii( SC_UNO_DATAFIELDCOUNT ) )       // read-only
        aRet <<= static_cast<sal_Int32>(nDataDimCount);
    else if (aNameStr.EqualsAscii(SC_UNO_GRANDTOTAL_NAME))
    {
        if (mpGrandTotalName.get())
            aRet <<= *mpGrandTotalName;
    }
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
	return aRet;
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )

// -----------------------------------------------------------------------

ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
	pSource( pSrc ),
	ppDims( NULL )
{
	//!	hold pSource

	// include data layout dimension and duplicated dimensions
	nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
}

ScDPDimensions::~ScDPDimensions()
{
	//!	release pSource

	if (ppDims)
	{
		for (long i=0; i<nDimCount; i++)
			if ( ppDims[i] )
				ppDims[i]->release();			// ref-counted
		delete[] ppDims;
	}
}

void ScDPDimensions::CountChanged()
{
	// include data layout dimension and duplicated dimensions
	long nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
	if ( ppDims )
	{
		long i;
		long nCopy = Min( nNewCount, nDimCount );
		ScDPDimension** ppNew = new ScDPDimension*[nNewCount];

		for (i=0; i<nCopy; i++)				// copy existing dims
			ppNew[i] = ppDims[i];
		for (i=nCopy; i<nNewCount; i++)		// clear additional pointers
			ppNew[i] = NULL;
		for (i=nCopy; i<nDimCount; i++)		// delete old dims if count is decreased
			if ( ppDims[i] )
				ppDims[i]->release();		// ref-counted

		delete[] ppDims;
		ppDims = ppNew;
	}
	nDimCount = nNewCount;
}

// very simple XNameAccess implementation using getCount/getByIndex

uno::Any SAL_CALL ScDPDimensions::getByName( const rtl::OUString& aName )
			throw(container::NoSuchElementException,
					lang::WrappedTargetException, uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
		{
			uno::Reference<container::XNamed> xNamed = getByIndex(i);
			uno::Any aRet;
			aRet <<= xNamed;
			return aRet;
		}

	throw container::NoSuchElementException();
//    return uno::Any();
}

uno::Sequence<rtl::OUString> SAL_CALL ScDPDimensions::getElementNames() throw(uno::RuntimeException)
{
	long nCount = getCount();
	uno::Sequence<rtl::OUString> aSeq(nCount);
	rtl::OUString* pArr = aSeq.getArray();
	for (long i=0; i<nCount; i++)
		pArr[i] = getByIndex(i)->getName();
	return aSeq;
}

sal_Bool SAL_CALL ScDPDimensions::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
			return sal_True;
	return sal_False;
}

uno::Type SAL_CALL ScDPDimensions::getElementType() throw(uno::RuntimeException)
{
	return getCppuType((uno::Reference<container::XNamed>*)0);
}

sal_Bool SAL_CALL ScDPDimensions::hasElements() throw(uno::RuntimeException)
{
	return ( getCount() > 0 );
}

// end of XNameAccess implementation

long ScDPDimensions::getCount() const
{
	//	in tabular data, every column of source data is a dimension

	return nDimCount;
}

ScDPDimension* ScDPDimensions::getByIndex(long nIndex) const
{
	if ( nIndex >= 0 && nIndex < nDimCount )
	{
		if ( !ppDims )
		{
			((ScDPDimensions*)this)->ppDims = new ScDPDimension*[nDimCount];
			for (long i=0; i<nDimCount; i++)
				ppDims[i] = NULL;
		}
		if ( !ppDims[nIndex] )
		{
			ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
			ppDims[nIndex]->acquire();		// ref-counted
		}

		return ppDims[nIndex];
	}

	return NULL;	//! exception?
}

// -----------------------------------------------------------------------

ScDPDimension::ScDPDimension( ScDPSource* pSrc, long nD ) :
	pSource( pSrc ),
	nDim( nD ),
	pHierarchies( NULL ),
	nUsedHier( 0 ),
	nFunction( SUBTOTAL_FUNC_SUM ),		// sum is default
    mpLayoutName(NULL),
    mpSubtotalName(NULL),
	nSourceDim( -1 ),
	bHasSelectedPage( sal_False ),
	pSelectedData( NULL ),
    mbHasHiddenMember(false)
{
	//!	hold pSource
}

ScDPDimension::~ScDPDimension()
{
	//!	release pSource

	if ( pHierarchies )
		pHierarchies->release();	// ref-counted

	delete pSelectedData;
}

ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
{
	if (!pHierarchies)
	{
		pHierarchies = new ScDPHierarchies( pSource, nDim );
		pHierarchies->acquire();		// ref-counted
	}
	return pHierarchies;
}

const rtl::OUString* ScDPDimension::GetLayoutName() const
{
    return mpLayoutName.get();
}

const rtl::OUString* ScDPDimension::GetSubtotalName() const
{
    return mpSubtotalName.get();
}

uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
													throw(uno::RuntimeException)
{
	return GetHierarchiesObject();
}

::rtl::OUString SAL_CALL ScDPDimension::getName() throw(uno::RuntimeException)
{
	if (aName.Len())
		return aName;
	else
		return pSource->GetData()->getDimensionName( nDim );
}

void SAL_CALL ScDPDimension::setName( const ::rtl::OUString& rNewName ) throw(uno::RuntimeException)
{
	//	used after cloning
	aName = String( rNewName );
}

sal_uInt16 ScDPDimension::getOrientation() const
{
	return pSource->GetOrientation( nDim );
}

void ScDPDimension::setOrientation(sal_uInt16 nNew)
{
	pSource->SetOrientation( nDim, nNew );
}

long ScDPDimension::getPosition() const
{
	return pSource->GetPosition( nDim );
}

void ScDPDimension::setPosition(long /* nNew */)
{
	//!	...
}

sal_Bool ScDPDimension::getIsDataLayoutDimension() const
{
	return pSource->GetData()->getIsDataLayoutDimension( nDim );
}

sal_uInt16 ScDPDimension::getFunction() const
{
	return nFunction;
}

void ScDPDimension::setFunction(sal_uInt16 nNew)
{
	nFunction = nNew;
}

long ScDPDimension::getUsedHierarchy() const
{
	return nUsedHier;
}

void ScDPDimension::setUsedHierarchy(long /* nNew */)
{
    // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
    // nUsedHier = nNew;
}

ScDPDimension* ScDPDimension::CreateCloneObject()
{
	DBG_ASSERT( nSourceDim < 0, "recursive duplicate - not implemented" );

	//!	set new name here, or temporary name ???
	String aNewName = aName;

	ScDPDimension* pNew = pSource->AddDuplicated( nDim, aNewName );

	pNew->aName = aNewName;				//! here or in source?
	pNew->nSourceDim = nDim;			//! recursive?

	return pNew;
}

uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone() throw(uno::RuntimeException)
{
	return CreateCloneObject();
}

sal_Bool ScDPDimension::isDuplicated() const
{
	return (nSourceDim >= 0);
}

const sheet::DataPilotFieldReference& ScDPDimension::GetReferenceValue() const
{
    return aReferenceValue;
}

const ScDPItemData& ScDPDimension::GetSelectedData()
{
	if ( !pSelectedData )
	{
		// find the named member to initialize pSelectedData from it, with name and value

		long nLevel = 0;		// same as in ScDPObject::FillPageList

		long nHierarchy = getUsedHierarchy();
		if ( nHierarchy >= GetHierarchiesObject()->getCount() )
			nHierarchy = 0;
		ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
		long nLevCount = pLevels->getCount();
		if ( nLevel < nLevCount )
		{
			ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();

			//! merge with ScDPMembers::getByName
			long nCount = pMembers->getCount();
			for (long i=0; i<nCount && !pSelectedData; i++)
			{
				ScDPMember* pMember = pMembers->getByIndex(i);
				if ( pMember->GetNameStr() == aSelectedPage )
				{
					pSelectedData = new ScDPItemData();
					pMember->FillItemData( *pSelectedData );
				}
			}
		}

		if ( !pSelectedData )
			pSelectedData = new ScDPItemData( aSelectedPage, 0.0, sal_False );		// default - name only
	}

	return *pSelectedData;
}

//UNUSED2009-05 sal_Bool ScDPDimension::IsValidPage( const ScDPItemData& rData )
//UNUSED2009-05 {
//UNUSED2009-05     if ( bHasSelectedPage )
//UNUSED2009-05         return rData.IsCaseInsEqual( GetSelectedData() );
//UNUSED2009-05 
//UNUSED2009-05     return sal_True;        // no selection -> all data
//UNUSED2009-05 }

sal_Bool ScDPDimension::IsVisible( const ScDPItemData& rData )
{
	if( ScDPMembers* pMembers = this->GetHierarchiesObject()->getByIndex(0)->
		GetLevelsObject()->getByIndex(0)->GetMembersObject() )
	{
		for( long i = pMembers->getCount()-1; i>=0; i-- )
			if( ScDPMember *pDPMbr = pMembers->getByIndex( i ) )
				if( rData.IsCaseInsEqual( pDPMbr->GetItemData() ) && !pDPMbr->getIsVisible() )
					return sal_False;
	}

	return sal_True;
}
// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
														throw(uno::RuntimeException)
{
	ScUnoGuard aGuard;

    static SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
	{
		{MAP_CHAR_LEN(SC_UNO_FILTER),	0,	&getCppuType((uno::Sequence<sheet::TableFilterField>*)0), 0, 0 },
        {MAP_CHAR_LEN(SC_UNO_FLAGS),    0,  &getCppuType((sal_Int32*)0),                beans::PropertyAttribute::READONLY, 0 },
		{MAP_CHAR_LEN(SC_UNO_FUNCTION),	0,	&getCppuType((sheet::GeneralFunction*)0),	0, 0 },
		{MAP_CHAR_LEN(SC_UNO_ISDATALA),	0,	&getBooleanCppuType(),						beans::PropertyAttribute::READONLY, 0 },
		{MAP_CHAR_LEN(SC_UNO_NUMBERFO),	0,	&getCppuType((sal_Int32*)0),				beans::PropertyAttribute::READONLY, 0 },
		{MAP_CHAR_LEN(SC_UNO_ORIENTAT),	0,	&getCppuType((sheet::DataPilotFieldOrientation*)0),	0, 0 },
		{MAP_CHAR_LEN(SC_UNO_ORIGINAL),	0,	&getCppuType((uno::Reference<container::XNamed>*)0), beans::PropertyAttribute::READONLY, 0 },
		{MAP_CHAR_LEN(SC_UNO_POSITION),	0,	&getCppuType((sal_Int32*)0),				0, 0 },
		{MAP_CHAR_LEN(SC_UNO_REFVALUE),	0,	&getCppuType((sheet::DataPilotFieldReference*)0), 0, 0 },
		{MAP_CHAR_LEN(SC_UNO_USEDHIER),	0,	&getCppuType((sal_Int32*)0),				0, 0 },
        {MAP_CHAR_LEN(SC_UNO_LAYOUTNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
        {MAP_CHAR_LEN(SC_UNO_FIELD_SUBTOTALNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
        {MAP_CHAR_LEN(SC_UNO_HAS_HIDDEN_MEMBER), 0, &getBooleanCppuType(), 0, 0 },
        {0,0,0,0,0,0}
	};
	static uno::Reference<beans::XPropertySetInfo> aRef =
		new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
	return aRef;
}

void SAL_CALL ScDPDimension::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue )
				throw(beans::UnknownPropertyException, beans::PropertyVetoException,
						lang::IllegalArgumentException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) )
	{
		sal_Int32 nInt = 0;
		if (aValue >>= nInt)
			setPosition( nInt );
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_USEDHIER ) )
	{
		sal_Int32 nInt = 0;
		if (aValue >>= nInt)
			setUsedHierarchy( nInt );
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_ORIENTAT ) )
	{
		sheet::DataPilotFieldOrientation eEnum;
		if (aValue >>= eEnum)
            setOrientation( sal::static_int_cast<sal_uInt16>(eEnum) );
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_FUNCTION ) )
	{
		sheet::GeneralFunction eEnum;
		if (aValue >>= eEnum)
            setFunction( sal::static_int_cast<sal_uInt16>(eEnum) );
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_REFVALUE ) )
        aValue >>= aReferenceValue;
	else if ( aNameStr.EqualsAscii( SC_UNO_FILTER ) )
	{
		sal_Bool bDone = sal_False;
		uno::Sequence<sheet::TableFilterField> aSeq;
		if (aValue >>= aSeq)
		{
			sal_Int32 nLength = aSeq.getLength();
			if ( nLength == 0 )
			{
				aSelectedPage.Erase();
				bHasSelectedPage = sal_False;
				bDone = sal_True;
			}
			else if ( nLength == 1 )
			{
				const sheet::TableFilterField& rField = aSeq[0];
				if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
				{
					aSelectedPage = rField.StringValue;
					bHasSelectedPage = sal_True;
					bDone = sal_True;
				}
			}
		}
		if ( !bDone )
		{
			DBG_ERROR("Filter property is not a single string");
			throw lang::IllegalArgumentException();
		}
		DELETEZ( pSelectedData );		// invalid after changing aSelectedPage
	}
    else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
    {
        OUString aTmpName;
        if (aValue >>= aTmpName)
            mpLayoutName.reset(new OUString(aTmpName));
    }
    else if (aNameStr.EqualsAscii(SC_UNO_FIELD_SUBTOTALNAME))
    {
        OUString aTmpName;
        if (aValue >>= aTmpName)
            mpSubtotalName.reset(new OUString(aTmpName));
    }
    else if (aNameStr.EqualsAscii(SC_UNO_HAS_HIDDEN_MEMBER))
        aValue >>= mbHasHiddenMember;
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
}

uno::Any SAL_CALL ScDPDimension::getPropertyValue( const rtl::OUString& aPropertyName )
				throw(beans::UnknownPropertyException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	uno::Any aRet;
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) )
		aRet <<= (sal_Int32) getPosition();
	else if ( aNameStr.EqualsAscii( SC_UNO_USEDHIER ) )
		aRet <<= (sal_Int32) getUsedHierarchy();
	else if ( aNameStr.EqualsAscii( SC_UNO_ORIENTAT ) )
	{
		sheet::DataPilotFieldOrientation eVal = (sheet::DataPilotFieldOrientation)getOrientation();
		aRet <<= eVal;
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_FUNCTION ) )
	{
		sheet::GeneralFunction eVal = (sheet::GeneralFunction)getFunction();
		aRet <<= eVal;
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_REFVALUE ) )
		aRet <<= aReferenceValue;
	else if ( aNameStr.EqualsAscii( SC_UNO_ISDATALA ) )					// read-only properties
		lcl_SetBoolInAny( aRet, getIsDataLayoutDimension() );
	else if ( aNameStr.EqualsAscii( SC_UNO_NUMBERFO ) )
    {
        sal_Int32 nFormat = 0;
        sheet::GeneralFunction eFunc = (sheet::GeneralFunction)getFunction();
        // #i63745# don't use source format for "count"
        if ( eFunc != sheet::GeneralFunction_COUNT && eFunc != sheet::GeneralFunction_COUNTNUMS )
            nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );

		switch ( aReferenceValue.ReferenceType )
		{
		case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
		case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
		case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
		case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
		case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
			nFormat = pSource->GetData()->GetNumberFormatByIdx( (NfIndexTableOffset)NF_PERCENT_DEC2 );
			break;
		case sheet::DataPilotFieldReferenceType::INDEX:
			nFormat = pSource->GetData()->GetNumberFormatByIdx( (NfIndexTableOffset)NF_NUMBER_SYSTEM );
			break;
		default:
			break;
		}

        aRet <<= nFormat;
    }
	else if ( aNameStr.EqualsAscii( SC_UNO_ORIGINAL ) )
	{
		uno::Reference<container::XNamed> xOriginal;
		if (nSourceDim >= 0)
			xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
		aRet <<= xOriginal;
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_FILTER ) )
	{
		if ( bHasSelectedPage )
		{
			// single filter field: first field equal to selected string
			sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
					sheet::FilterOperator_EQUAL, sal_False, 0.0, aSelectedPage );
			aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
		}
		else
			aRet <<= uno::Sequence<sheet::TableFilterField>(0);
	}
    else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
        aRet <<= mpLayoutName.get() ? *mpLayoutName : OUString::createFromAscii("");
    else if (aNameStr.EqualsAscii(SC_UNO_FIELD_SUBTOTALNAME))
        aRet <<= mpSubtotalName.get() ? *mpSubtotalName : OUString::createFromAscii("");
    else if (aNameStr.EqualsAscii(SC_UNO_HAS_HIDDEN_MEMBER))
        aRet <<= mbHasHiddenMember;
    else if (aNameStr.EqualsAscii(SC_UNO_FLAGS))
    {
        sal_Int32 nFlags = 0;       // tabular data: all orientations are possible
        aRet <<= nFlags;
    }
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
	return aRet;
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )

// -----------------------------------------------------------------------

ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, long nD ) :
	pSource( pSrc ),
	nDim( nD ),
	ppHiers( NULL )
{
	//!	hold pSource

#if 0
	//	date columns have 3 hierarchies (flat/quarter/week), other columns only one
	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( pSource->IsDateDimension( nSrcDim ) )
		nHierCount = SC_DAPI_DATE_HIERARCHIES;
	else
		nHierCount = 1;
#endif

    // #i52547# don't offer the incomplete date hierarchy implementation
    nHierCount = 1;
}

ScDPHierarchies::~ScDPHierarchies()
{
	//!	release pSource

	if (ppHiers)
	{
		for (long i=0; i<nHierCount; i++)
			if ( ppHiers[i] )
				ppHiers[i]->release();		// ref-counted
		delete[] ppHiers;
	}
}

// very simple XNameAccess implementation using getCount/getByIndex

uno::Any SAL_CALL ScDPHierarchies::getByName( const rtl::OUString& aName )
			throw(container::NoSuchElementException,
					lang::WrappedTargetException, uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
		{
			uno::Reference<container::XNamed> xNamed = getByIndex(i);
			uno::Any aRet;
			aRet <<= xNamed;
			return aRet;
		}

	throw container::NoSuchElementException();
//    return uno::Any();
}

uno::Sequence<rtl::OUString> SAL_CALL ScDPHierarchies::getElementNames() throw(uno::RuntimeException)
{
	long nCount = getCount();
	uno::Sequence<rtl::OUString> aSeq(nCount);
	rtl::OUString* pArr = aSeq.getArray();
	for (long i=0; i<nCount; i++)
		pArr[i] = getByIndex(i)->getName();
	return aSeq;
}

sal_Bool SAL_CALL ScDPHierarchies::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
			return sal_True;
	return sal_False;
}

uno::Type SAL_CALL ScDPHierarchies::getElementType() throw(uno::RuntimeException)
{
	return getCppuType((uno::Reference<container::XNamed>*)0);
}

sal_Bool SAL_CALL ScDPHierarchies::hasElements() throw(uno::RuntimeException)
{
	return ( getCount() > 0 );
}

// end of XNameAccess implementation

long ScDPHierarchies::getCount() const
{
	return nHierCount;
}

ScDPHierarchy* ScDPHierarchies::getByIndex(long nIndex) const
{
	//	pass hierarchy index to new object in case the implementation
	//	will be extended to more than one hierarchy

	if ( nIndex >= 0 && nIndex < nHierCount )
	{
		if ( !ppHiers )
		{
			((ScDPHierarchies*)this)->ppHiers = new ScDPHierarchy*[nHierCount];
			for (long i=0; i<nHierCount; i++)
				ppHiers[i] = NULL;
		}
		if ( !ppHiers[nIndex] )
		{
			ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
			ppHiers[nIndex]->acquire();			// ref-counted
		}

		return ppHiers[nIndex];
	}

	return NULL;	//! exception?
}

// -----------------------------------------------------------------------

ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, long nD, long nH ) :
	pSource( pSrc ),
	nDim( nD ),
	nHier( nH ),
	pLevels( NULL )
{
	//!	hold pSource
}

ScDPHierarchy::~ScDPHierarchy()
{
	//!	release pSource

	if (pLevels)
		pLevels->release();		// ref-counted
}

ScDPLevels* ScDPHierarchy::GetLevelsObject()
{
	if (!pLevels)
	{
		pLevels = new ScDPLevels( pSource, nDim, nHier );
		pLevels->acquire();		// ref-counted
	}
	return pLevels;
}

uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
													throw(uno::RuntimeException)
{
	return GetLevelsObject();
}

::rtl::OUString SAL_CALL ScDPHierarchy::getName() throw(uno::RuntimeException)
{
	String aRet;		//!	globstr-ID !!!!
	switch (nHier)
	{
		case SC_DAPI_HIERARCHY_FLAT:
			aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("flat"));
			break;	//! name ???????
		case SC_DAPI_HIERARCHY_QUARTER:
			aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Quarter"));
			break;	//! name ???????
		case SC_DAPI_HIERARCHY_WEEK:
			aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Week"));
			break;	//! name ???????
		default:
			DBG_ERROR( "ScDPHierarchy::getName: unexpected hierarchy" );
			break;
	}
	return aRet;
}

void SAL_CALL ScDPHierarchy::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException)
{
	DBG_ERROR("not implemented");		//! exception?
}

// -----------------------------------------------------------------------

ScDPLevels::ScDPLevels( ScDPSource* pSrc, long nD, long nH ) :
	pSource( pSrc ),
	nDim( nD ),
	nHier( nH ),
	ppLevs( NULL )
{
	//!	hold pSource

	//	text columns have only one level

	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( pSource->IsDateDimension( nSrcDim ) )
	{
		switch ( nHier )
		{
			case SC_DAPI_HIERARCHY_FLAT:	nLevCount = SC_DAPI_FLAT_LEVELS;	break;
			case SC_DAPI_HIERARCHY_QUARTER:	nLevCount = SC_DAPI_QUARTER_LEVELS;	break;
			case SC_DAPI_HIERARCHY_WEEK:	nLevCount = SC_DAPI_WEEK_LEVELS;	break;
			default:
				DBG_ERROR("wrong hierarchy");
				nLevCount = 0;
		}
	}
	else
		nLevCount = 1;
}

ScDPLevels::~ScDPLevels()
{
	//!	release pSource

	if (ppLevs)
	{
		for (long i=0; i<nLevCount; i++)
			if ( ppLevs[i] )
				ppLevs[i]->release();	// ref-counted
		delete[] ppLevs;
	}
}

// very simple XNameAccess implementation using getCount/getByIndex

uno::Any SAL_CALL ScDPLevels::getByName( const rtl::OUString& aName )
			throw(container::NoSuchElementException,
					lang::WrappedTargetException, uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
		{
			uno::Reference<container::XNamed> xNamed = getByIndex(i);
			uno::Any aRet;
			aRet <<= xNamed;
			return aRet;
		}

	throw container::NoSuchElementException();
//    return uno::Any();
}

uno::Sequence<rtl::OUString> SAL_CALL ScDPLevels::getElementNames() throw(uno::RuntimeException)
{
	long nCount = getCount();
	uno::Sequence<rtl::OUString> aSeq(nCount);
	rtl::OUString* pArr = aSeq.getArray();
	for (long i=0; i<nCount; i++)
		pArr[i] = getByIndex(i)->getName();
	return aSeq;
}

sal_Bool SAL_CALL ScDPLevels::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException)
{
	long nCount = getCount();
	for (long i=0; i<nCount; i++)
		if ( getByIndex(i)->getName() == aName )
			return sal_True;
	return sal_False;
}

uno::Type SAL_CALL ScDPLevels::getElementType() throw(uno::RuntimeException)
{
	return getCppuType((uno::Reference<container::XNamed>*)0);
}

sal_Bool SAL_CALL ScDPLevels::hasElements() throw(uno::RuntimeException)
{
	return ( getCount() > 0 );
}

// end of XNameAccess implementation

long ScDPLevels::getCount() const
{
	return nLevCount;
}

ScDPLevel* ScDPLevels::getByIndex(long nIndex) const
{
	if ( nIndex >= 0 && nIndex < nLevCount )
	{
		if ( !ppLevs )
		{
			((ScDPLevels*)this)->ppLevs = new ScDPLevel*[nLevCount];
			for (long i=0; i<nLevCount; i++)
				ppLevs[i] = NULL;
		}
		if ( !ppLevs[nIndex] )
		{
			ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
			ppLevs[nIndex]->acquire();		// ref-counted
		}

		return ppLevs[nIndex];
	}

	return NULL;	//! exception?
}

// -----------------------------------------------------------------------

class ScDPGlobalMembersOrder
{
    ScDPLevel&  rLevel;
    sal_Bool        bAscending;

public:
            ScDPGlobalMembersOrder( ScDPLevel& rLev, sal_Bool bAsc ) :
                rLevel(rLev),
                bAscending(bAsc)
            {}
            ~ScDPGlobalMembersOrder() {}

    sal_Bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
};

sal_Bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
{
    sal_Int32 nCompare = 0;
    // seems that some ::std::sort() implementations pass the same index twice
    if( nIndex1 != nIndex2 )
    {
        ScDPMembers* pMembers = rLevel.GetMembersObject();
        ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
        ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
        nCompare = pMember1->Compare( *pMember2 );
    }
    return bAscending ? (nCompare < 0) : (nCompare > 0);
}

// -----------------------------------------------------------------------

ScDPLevel::ScDPLevel( ScDPSource* pSrc, long nD, long nH, long nL ) :
	pSource( pSrc ),
	nDim( nD ),
	nHier( nH ),
	nLev( nL ),
	pMembers( NULL ),
	bShowEmpty( sal_False ),
    aSortInfo( EMPTY_STRING, sal_True, sheet::DataPilotFieldSortMode::NAME ),   // default: sort by name
	nSortMeasure( 0 ),
	nAutoMeasure( 0 ),
	bEnableLayout( sal_False )
{
	//!	hold pSource
	//	aSubTotals is empty
}

ScDPLevel::~ScDPLevel()
{
	//!	release pSource

	if ( pMembers )
		pMembers->release();	// ref-counted
}

void ScDPLevel::EvaluateSortOrder()
{
    switch (aSortInfo.Mode)
    {
        case sheet::DataPilotFieldSortMode::DATA:
            {
                // find index of measure (index among data dimensions)

                String aDataFieldName = aSortInfo.Field;
                long nMeasureCount = pSource->GetDataDimensionCount();
                for (long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
                {
                    if ( pSource->GetDataDimName(nMeasure) == aDataFieldName )
                    {
                        nSortMeasure = nMeasure;
                        break;
                    }
                }

                //! error if not found?
            }
            break;
        case sheet::DataPilotFieldSortMode::MANUAL:
        case sheet::DataPilotFieldSortMode::NAME:
            {
                ScDPMembers* pLocalMembers = GetMembersObject();
                long nCount = pLocalMembers->getCount();

//                DBG_ASSERT( aGlobalOrder.empty(), "sort twice?" );
                aGlobalOrder.resize( nCount );
                for (long nPos=0; nPos<nCount; nPos++)
                    aGlobalOrder[nPos] = nPos;

                // allow manual or name (manual is always ascending)
                sal_Bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
                ScDPGlobalMembersOrder aComp( *this, bAscending );
                ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
            }
            break;
    }

    if ( aAutoShowInfo.IsEnabled )
    {
        // find index of measure (index among data dimensions)

        String aDataFieldName = aAutoShowInfo.DataField;
        long nMeasureCount = pSource->GetDataDimensionCount();
        for (long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
        {
            if ( pSource->GetDataDimName(nMeasure) == aDataFieldName )
            {
                nAutoMeasure = nMeasure;
                break;
            }
        }

        //! error if not found?
    }
}

void ScDPLevel::SetEnableLayout( sal_Bool bSet )
{
    bEnableLayout = bSet;
}

ScDPMembers* ScDPLevel::GetMembersObject()
{
	if (!pMembers)
	{
		pMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
		pMembers->acquire();	// ref-counted
	}
	return pMembers;
}

uno::Reference<container::XNameAccess> SAL_CALL ScDPLevel::getMembers() throw(uno::RuntimeException)
{
	return GetMembersObject();
}

uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults() throw(uno::RuntimeException)
{
	const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
	if (pRes)
		return *pRes;

	return uno::Sequence<sheet::MemberResult>(0);		//! Error?
}

::rtl::OUString SAL_CALL ScDPLevel::getName() throw(uno::RuntimeException)
{
	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( pSource->IsDateDimension( nSrcDim ) )
	{
		String aRet;		//!	globstr-ID !!!!

		if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
		{
			switch ( nLev )
			{
				case SC_DAPI_LEVEL_YEAR:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Year"));
					break;
				case SC_DAPI_LEVEL_QUARTER:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Quarter"));
					break;
				case SC_DAPI_LEVEL_MONTH:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Month"));
					break;
				case SC_DAPI_LEVEL_DAY:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Day"));
					break;
				default:
					DBG_ERROR( "ScDPLevel::getName: unexpected level" );
					break;
			}
		}
		else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
		{
			switch ( nLev )
			{
				case SC_DAPI_LEVEL_YEAR:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Year"));
					break;
				case SC_DAPI_LEVEL_WEEK:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Week"));
					break;
				case SC_DAPI_LEVEL_WEEKDAY:
					aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Weekday"));
					break;
				default:
					DBG_ERROR( "ScDPLevel::getName: unexpected level" );
					break;
			}
		}
		if (aRet.Len())
			return aRet;
	}

    ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
    if (!pDim)
        return rtl::OUString();

    return pDim->getName();
}

void SAL_CALL ScDPLevel::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException)
{
	DBG_ERROR("not implemented");		//! exception?
}

uno::Sequence<sheet::GeneralFunction> ScDPLevel::getSubTotals() const
{
	//!	separate functions for settings and evaluation?

	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( !pSource->SubTotalAllowed( nSrcDim ) )
		return uno::Sequence<sheet::GeneralFunction>(0);

	return aSubTotals;
}

void ScDPLevel::setSubTotals(const uno::Sequence<sheet::GeneralFunction>& rNew)
{
	aSubTotals = rNew;
	//!	set "manual change" flag?
}

sal_Bool ScDPLevel::getShowEmpty() const
{
	return bShowEmpty;
}

void ScDPLevel::setShowEmpty(sal_Bool bSet)
{
	bShowEmpty = bSet;
}

// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
														throw(uno::RuntimeException)
{
	ScUnoGuard aGuard;

    static SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
	{
		//! change type of AutoShow/Layout/Sorting to API struct when available
		{MAP_CHAR_LEN(SC_UNO_AUTOSHOW),	0,	&getCppuType((sheet::DataPilotFieldAutoShowInfo*)0),     0, 0 },
		{MAP_CHAR_LEN(SC_UNO_LAYOUT),	0,	&getCppuType((sheet::DataPilotFieldLayoutInfo*)0),       0, 0 },
		{MAP_CHAR_LEN(SC_UNO_SHOWEMPT),	0,	&getBooleanCppuType(),									 0, 0 },
		{MAP_CHAR_LEN(SC_UNO_SORTING),	0,	&getCppuType((sheet::DataPilotFieldSortInfo*)0),         0, 0 },
		{MAP_CHAR_LEN(SC_UNO_SUBTOTAL),	0,	&getCppuType((uno::Sequence<sheet::GeneralFunction>*)0), 0, 0 },
        {0,0,0,0,0,0}
	};
	static uno::Reference<beans::XPropertySetInfo> aRef =
		new SfxItemPropertySetInfo( aDPLevelMap_Impl );
	return aRef;
}

void SAL_CALL ScDPLevel::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue )
				throw(beans::UnknownPropertyException, beans::PropertyVetoException,
						lang::IllegalArgumentException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_SHOWEMPT ) )
		setShowEmpty( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_SUBTOTAL ) )
	{
		uno::Sequence<sheet::GeneralFunction> aSeq;
		if ( aValue >>= aSeq )
			setSubTotals( aSeq );
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_SORTING ) )
        aValue >>= aSortInfo;
	else if ( aNameStr.EqualsAscii( SC_UNO_AUTOSHOW ) )
	    aValue >>= aAutoShowInfo;
	else if ( aNameStr.EqualsAscii( SC_UNO_LAYOUT ) )
	    aValue >>= aLayoutInfo;
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
}

uno::Any SAL_CALL ScDPLevel::getPropertyValue( const rtl::OUString& aPropertyName )
				throw(beans::UnknownPropertyException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	uno::Any aRet;
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_SHOWEMPT ) )
		lcl_SetBoolInAny( aRet, getShowEmpty() );
	else if ( aNameStr.EqualsAscii( SC_UNO_SUBTOTAL ) )
	{
		uno::Sequence<sheet::GeneralFunction> aSeq = getSubTotals();		//! avoid extra copy?
		aRet <<= aSeq;
	}
	else if ( aNameStr.EqualsAscii( SC_UNO_SORTING ) )
        aRet <<= aSortInfo;
	else if ( aNameStr.EqualsAscii( SC_UNO_AUTOSHOW ) )
	    aRet <<= aAutoShowInfo;
	else if ( aNameStr.EqualsAscii( SC_UNO_LAYOUT ) )
	    aRet <<= aLayoutInfo;
    else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
    {
        // read only property
        long nSrcDim = pSource->GetSourceDim(nDim);
        ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
        if (!pDim)
            return aRet;

        const OUString* pLayoutName = pDim->GetLayoutName();
        if (!pLayoutName)
            return aRet;

        aRet <<= *pLayoutName;
    }
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
	return aRet;
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )

// -----------------------------------------------------------------------

ScDPMembers::ScDPMembers( ScDPSource* pSrc, long nD, long nH, long nL ) :
	pSource( pSrc ),
	nDim( nD ),
	nHier( nH ),
	nLev( nL ),
	ppMbrs( NULL )
{
	//!	hold pSource

	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( pSource->IsDataLayoutDimension(nSrcDim) )
		nMbrCount = pSource->GetDataDimensionCount();
	else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
	{
		nMbrCount = 0;
		if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
		{
			switch (nLev)
			{
				case SC_DAPI_LEVEL_YEAR:
                    {
                        // Wang Xu Ming - DataPilot migration
                        const ScDPItemData* pLastNumData = NULL;
                        for ( SCROW n = 0 ;n <GetSrcItemsCount() ; n-- )
                        {
                            const ScDPItemData* pData  = GetSrcItemDataByIndex( n );
                            if ( pData && pData->HasStringData() )
                                break;
                            else
                                pLastNumData = pData;
                        }
                        // End Comments

                        if ( pLastNumData )
						{
                            const ScDPItemData*  pFirstData = GetSrcItemDataByIndex( 0 );
                            double fFirstVal = pFirstData->GetValue();
							double fLastVal = pLastNumData->GetValue();

							long nFirstYear = pSource->GetData()->GetDatePart(
										(long)::rtl::math::approxFloor( fFirstVal ),
										nHier, nLev );
							long nLastYear = pSource->GetData()->GetDatePart(
										(long)::rtl::math::approxFloor( fLastVal ),
										nHier, nLev );

							nMbrCount = nLastYear + 1 - nFirstYear;
						}
						else
							nMbrCount = 0;		// no values
					}
					break;
				case SC_DAPI_LEVEL_QUARTER:	nMbrCount = 4;	break;
				case SC_DAPI_LEVEL_MONTH:	nMbrCount = 12;	break;
				case SC_DAPI_LEVEL_DAY:		nMbrCount = 31;	break;
				default:
					DBG_ERROR( "ScDPMembers::ScDPMembers: unexpected level" );
					break;
			}
		}
		else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
		{
			switch (nLev)
			{
				case SC_DAPI_LEVEL_YEAR:	nMbrCount = 1;	break;		//! get years from source
				case SC_DAPI_LEVEL_WEEK:	nMbrCount = 53;	break;
				case SC_DAPI_LEVEL_WEEKDAY:	nMbrCount = 7;	break;
				default:
					DBG_ERROR( "ScDPMembers::ScDPMembers: unexpected level" );
					break;
			}
		}
	}
	else
		nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
}

ScDPMembers::~ScDPMembers()
{
	//!	release pSource

	if (ppMbrs)
	{
		for (long i=0; i<nMbrCount; i++)
			if ( ppMbrs[i] )
				ppMbrs[i]->release();	// ref-counted
		delete[] ppMbrs;
	}
}

// XNameAccess implementation using getCount/getByIndex

sal_Int32 ScDPMembers::GetIndexFromName( const ::rtl::OUString& rName ) const
{
    if ( aHashMap.empty() )
    {
        // store the index for each name

        sal_Int32 nCount = getCount();
        for (sal_Int32 i=0; i<nCount; i++)
            aHashMap[ getByIndex(i)->getName() ] = i;
    }

    ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
    if ( aIter != aHashMap.end() )
        return aIter->second;           // found index
    else
        return -1;                      // not found
}

uno::Any SAL_CALL ScDPMembers::getByName( const rtl::OUString& aName )
			throw(container::NoSuchElementException,
					lang::WrappedTargetException, uno::RuntimeException)
{
    sal_Int32 nIndex = GetIndexFromName( aName );
    if ( nIndex >= 0 )
    {
        uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
        uno::Any aRet;
        aRet <<= xNamed;
        return aRet;
    }

	throw container::NoSuchElementException();
//    return uno::Any();
}

uno::Sequence<rtl::OUString> SAL_CALL ScDPMembers::getElementNames() throw(uno::RuntimeException)
{
    // Return list of names in sorted order,
    // so it's displayed in that order in the field options dialog.
    // Sorting is done at the level object (parent of this).

    ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
        GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
    pLevel->EvaluateSortOrder();
    const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
    bool bSort = !rGlobalOrder.empty();

	long nCount = getCount();
	uno::Sequence<rtl::OUString> aSeq(nCount);
	rtl::OUString* pArr = aSeq.getArray();
	for (long i=0; i<nCount; i++)
        pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->getName();
	return aSeq;
}

sal_Bool SAL_CALL ScDPMembers::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException)
{
    return ( GetIndexFromName( aName ) >= 0 );
}

uno::Type SAL_CALL ScDPMembers::getElementType() throw(uno::RuntimeException)
{
	return getCppuType((uno::Reference<container::XNamed>*)0);
}

sal_Bool SAL_CALL ScDPMembers::hasElements() throw(uno::RuntimeException)
{
	return ( getCount() > 0 );
}

// end of XNameAccess implementation

long ScDPMembers::getCount() const
{
	return nMbrCount;
}

long ScDPMembers::getMinMembers() const
{
	// used in lcl_CountMinMembers

	long nVisCount = 0;
	if ( ppMbrs )
	{
		for (long i=0; i<nMbrCount; i++)
		{
			//	count only visible with details (default is true for both)
			const ScDPMember* pMbr = ppMbrs[i];
			if ( !pMbr || ( pMbr->getIsVisible() && pMbr->getShowDetails() ) )
				++nVisCount;
		}
	}
	else
		nVisCount = nMbrCount;		// default for all

	return nVisCount;
}

ScDPMember* ScDPMembers::getByIndex(long nIndex) const
{
	//	result of GetColumnEntries must not change between ScDPMembers ctor
	//	and all calls to getByIndex

	if ( nIndex >= 0 && nIndex < nMbrCount )
	{
		if ( !ppMbrs )
		{
			((ScDPMembers*)this)->ppMbrs = new ScDPMember*[nMbrCount];
			for (long i=0; i<nMbrCount; i++)
				ppMbrs[i] = NULL;
		}
		if ( !ppMbrs[nIndex] )
		{
			ScDPMember* pNew;
			long nSrcDim = pSource->GetSourceDim( nDim );
			if ( pSource->IsDataLayoutDimension(nSrcDim) )
			{
				// empty name (never shown, not used for lookup)
                pNew = new ScDPMember( pSource, nDim, nHier, nLev, 0 );
			}
			else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
			{
				long nVal = 0;
				String aName;

				if ( nLev == SC_DAPI_LEVEL_YEAR )	// YEAR is in both hierarchies
				{
					//!	cache year range here!
					
					// Wang Xu Ming - DataPilot migration
					double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
					long nFirstYear = pSource->GetData()->GetDatePart(
										(long)::rtl::math::approxFloor( fFirstVal ),
										nHier, nLev );

					// End Comments
					nVal = nFirstYear + nIndex;
				}
				else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
				{
					nVal = nIndex;				// DayOfWeek is 0-based
					aName = ScGlobal::GetCalendar()->getDisplayName(
						::com::sun::star::i18n::CalendarDisplayIndex::DAY,
                        sal::static_int_cast<sal_Int16>(nVal), 0 );
				}
				else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
				{
					nVal = nIndex;				// Month is 0-based
					aName = ScGlobal::GetCalendar()->getDisplayName(
						::com::sun::star::i18n::CalendarDisplayIndex::MONTH,
                        sal::static_int_cast<sal_Int16>(nVal), 0 );
				}
				else
					nVal = nIndex + 1;			// Quarter, Day, Week are 1-based

				if ( !aName.Len() )
					aName = String::CreateFromInt32(nVal);
                   
                		    ScDPItemData  rData( aName, nVal, sal_True, 0 ) ;
				    pNew = new ScDPMember( pSource, nDim, nHier, nLev, pSource->GetCache()->GetAdditionalItemID(rData));
			}
			else
			{
			         const std::vector< SCROW >& memberIndexs = pSource->GetData()->GetColumnEntries( nSrcDim );
				    pNew = new ScDPMember( pSource, nDim, nHier, nLev, memberIndexs[nIndex]   );
			}
			pNew->acquire();			// ref-counted
			ppMbrs[nIndex] = pNew;
		}

		DBG_ASSERT( ppMbrs[nIndex] ," member is not initialized " );

		return ppMbrs[nIndex];
	}

	return NULL;	//! exception?
}

// -----------------------------------------------------------------------

ScDPMember::ScDPMember( ScDPSource* pSrc, long nD, long nH, long nL,
						SCROW nIndex /*const String& rN, double fV, sal_Bool bHV*/ ) :
	pSource( pSrc ),
	nDim( nD ),
	nHier( nH ),
	nLev( nL ),
    mnDataId( nIndex ),
    mpLayoutName(NULL),
    nPosition( -1 ),
	bVisible( sal_True ),
	bShowDet( sal_True )
{
	//!	hold pSource
}

ScDPMember::~ScDPMember()
{
	//!	release pSource
}

sal_Bool ScDPMember::IsNamedItem( const ScDPItemData& r ) const
{
	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) && r.IsValue() )
	{
		long nComp = pSource->GetData()->GetDatePart(
										(long)::rtl::math::approxFloor( r.GetValue() ),
										nHier, nLev );

		//	fValue is converted from integer, so simple comparison works
		return nComp == GetItemData().GetValue();
	}

	return r.IsCaseInsEqual( GetItemData() );
}

sal_Bool ScDPMember::IsNamedItem( SCROW    nIndex ) const
{
	long nSrcDim = pSource->GetSourceDim( nDim );
	if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
	{ 
        const ScDPItemData* pData =  pSource->GetCache()->GetItemDataById( (SCCOL) nSrcDim, nIndex );
        if (  pData->IsValue()  )
        {
            long nComp = pSource->GetData()->GetDatePart(
                (long)::rtl::math::approxFloor( pData->GetValue() ),
                nHier, nLev );
            //  fValue is converted from integer, so simple comparison works
            return nComp == GetItemData().GetValue();
        }
    }

	return  nIndex == mnDataId;
}

sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
{
    if ( nPosition >= 0 )
    {
        if ( rOther.nPosition >= 0 )
        {
            DBG_ASSERT( nPosition != rOther.nPosition, "same position for two members" );
            return ( nPosition < rOther.nPosition ) ? -1 : 1;
        }
        else
        {
            // only this has a position - members with specified positions come before those without
            return -1;
        }
    }
    else if ( rOther.nPosition >= 0 )
    {
        // only rOther has a position
        return 1;
    }

    // no positions set - compare names
   return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
}

void ScDPMember::FillItemData( ScDPItemData& rData ) const
{
	//!	handle date hierarchy...

	rData = GetItemData() ;
}

const OUString* ScDPMember::GetLayoutName() const
{
    return mpLayoutName.get();
}

String ScDPMember::GetNameStr() const
{
	  return GetItemData().GetString();
}

::rtl::OUString SAL_CALL ScDPMember::getName() throw(uno::RuntimeException)
{
	  return GetItemData().GetString();
}

void SAL_CALL ScDPMember::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException)
{
	DBG_ERROR("not implemented");		//! exception?
}

sal_Bool ScDPMember::getIsVisible() const
{
	return bVisible;
}

void ScDPMember::setIsVisible(sal_Bool bSet)
{
	bVisible = bSet;
	//!	set "manual change" flag
}

sal_Bool ScDPMember::getShowDetails() const
{
	return bShowDet;
}

void ScDPMember::setShowDetails(sal_Bool bSet)
{
	bShowDet = bSet;
	//!	set "manual change" flag
}

sal_Int32 ScDPMember::getPosition() const
{
    return nPosition;
}

void ScDPMember::setPosition(sal_Int32 nNew)
{
    nPosition = nNew;
}

// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
														throw(uno::RuntimeException)
{
	ScUnoGuard aGuard;

    static SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
	{
		{MAP_CHAR_LEN(SC_UNO_ISVISIBL),	0,	&getBooleanCppuType(),				0, 0 },
        {MAP_CHAR_LEN(SC_UNO_POSITION), 0,  &getCppuType((sal_Int32*)0),        0, 0 },
		{MAP_CHAR_LEN(SC_UNO_SHOWDETA),	0,	&getBooleanCppuType(),				0, 0 },
        {MAP_CHAR_LEN(SC_UNO_LAYOUTNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
        {0,0,0,0,0,0}
	};
	static uno::Reference<beans::XPropertySetInfo> aRef =
		new SfxItemPropertySetInfo( aDPMemberMap_Impl );
	return aRef;
}

void SAL_CALL ScDPMember::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue )
				throw(beans::UnknownPropertyException, beans::PropertyVetoException,
						lang::IllegalArgumentException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_ISVISIBL ) )
		setIsVisible( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_SHOWDETA ) )
		setShowDetails( lcl_GetBoolFromAny( aValue ) );
	else if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) )
	{
        sal_Int32 nInt = 0;
        if (aValue >>= nInt)
            setPosition( nInt );
	}
    else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
    {
        rtl::OUString aName;
        if (aValue >>= aName)
            mpLayoutName.reset(new rtl::OUString(aName));
    }
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
}

uno::Any SAL_CALL ScDPMember::getPropertyValue( const rtl::OUString& aPropertyName )
				throw(beans::UnknownPropertyException, lang::WrappedTargetException,
						uno::RuntimeException)
{
	uno::Any aRet;
	String aNameStr = aPropertyName;
	if ( aNameStr.EqualsAscii( SC_UNO_ISVISIBL ) )
		lcl_SetBoolInAny( aRet, getIsVisible() );
	else if ( aNameStr.EqualsAscii( SC_UNO_SHOWDETA ) )
		lcl_SetBoolInAny( aRet, getShowDetails() );
    else if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) )
        aRet <<= (sal_Int32) getPosition();
    else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
        aRet <<= mpLayoutName.get() ? *mpLayoutName : rtl::OUString();
	else
	{
		DBG_ERROR("unknown property");
		//!	THROW( UnknownPropertyException() );
	}
	return aRet;
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )


ScDPTableDataCache* ScDPSource::GetCache()
{
	DBG_ASSERT( GetData() , "empty ScDPTableData pointer");
	return ( GetData()!=NULL) ? GetData()->GetCacheTable().GetCache() : NULL ;
}

const ScDPItemData& ScDPMember::GetItemData() const
{
    return *pSource->GetItemDataById( (SCCOL)nDim, mnDataId );//ms-cache-core
}

const ScDPItemData* ScDPSource::GetItemDataById(long nDim, long nId)
{
    long nSrcDim = GetSourceDim( nDim ); 
    const ScDPItemData* pItemData = GetData()->GetMemberById(  nSrcDim,  nId );
    if ( !pItemData )
   { //todo:
   	    ScDPItemData item;
   	    nId = GetCache()->GetAdditionalItemID( item );
        pItemData = GetData()->GetMemberById(  nSrcDim,  nId );
    }
   return pItemData;
}

SCROW  ScDPSource::GetMemberId(  long  nDim, const ScDPItemData& rData )
{
	long nSrcDim = GetSourceDim( nDim );
       return  GetCache()->GetIdByItemData(  nSrcDim, rData ); 
}

const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex( SCROW nIndex)
{
    const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
    if ( nIndex >= (long )(memberIds.size()) || nIndex < 0 )
        return NULL;
    SCROW nId =  memberIds[ nIndex ];                  
    return pSource->GetItemDataById( nDim, nId );
}

 SCROW ScDPMembers::GetSrcItemsCount()
 {
    return pSource->GetData()->GetColumnEntries( nDim ).size();
 }
// End Comments