/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * 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 <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>

#include <tools/debug.hxx>
#include <rtl/math.hxx>
#include <svl/itemprop.hxx>
#include <svl/intitem.hxx>
#include <vcl/svapp.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 "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 ::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 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( false ),
    bRepeatIfEmpty( false ),
    nDupCount( 0 ),
    pResData( NULL ),
    pColResRoot( NULL ),
    pRowResRoot( NULL ),
    pColResults( NULL ),
    pRowResults( NULL ),
    bResultOverflow( 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;
}

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 = 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 = false;
            }

            return sal_True;	// found
        }
    return 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;
            // DataPilot Migration - Cache&&Performance
        case sheet::DataPilotFieldOrientation_HIDDEN:
            break;
        default: 
            OSL_FAIL( "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);
}

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;
        }
    }

    OSL_FAIL("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)
{
    OSL_FAIL("not implemented");	//! exception?
}

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

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

    typedef boost::unordered_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;
    boost::unordered_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 = 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] )
        {
            OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
            return 0;
        }

        sal_Bool bDo = 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 = 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(boost::unordered_set<sal_Int32>& rCatDims)
{
    boost::unordered_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())
    {
        boost::unordered_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 = 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, bColumnGrand );
        pRowResRoot = new ScDPResultMember( pResData, 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( 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 );

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


void ScDPSource::FillLevelList( sal_uInt16 nOrientation, std::vector<ScDPLevel*> &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:
            OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
            break;
    }
    if (!pDimIndex)
    {
        OSL_FAIL("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.push_back(pLevel);
        }
    }
}

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.size();
        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);

            long nPos = 0;
            pColResRoot->FillMemberResults( pColResults, nPos, pResData->GetColStartMeasure(),
                                            sal_True, NULL, NULL );
        }

        FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
        long nRowLevelCount = aRowLevelList.size();
        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);

            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 = 0;
    long nColCount = aColLevelList.size();
    for (i=0; i<nColCount; i++)
    {
        ScDPLevel* pColLevel = aColLevelList[i];
        if ( pColLevel == pLevel )
            return pColResults+i;
    }
    long nRowCount = aRowLevelList.size();
    for (i=0; i<nRowCount; i++)
    {
        ScDPLevel* pRowLevel = aRowLevelList[i];
        if ( pRowLevel == pLevel )
            return pRowResults+i;
    }
    return NULL;
}

// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
                                                        throw(uno::RuntimeException)
{
    SolarMutexGuard 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
    {
        OSL_FAIL("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
    {
        OSL_FAIL("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 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( 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
}

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, false );		// default - name only
    }

    return *pSelectedData;
}

// XPropertySet

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
                                                        throw(uno::RuntimeException)
{
    SolarMutexGuard 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 = false;
        uno::Sequence<sheet::TableFilterField> aSeq;
        if (aValue >>= aSeq)
        {
            sal_Int32 nLength = aSeq.getLength();
            if ( nLength == 0 )
            {
                aSelectedPage.Erase();
                bHasSelectedPage = 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 )
        {
            OSL_FAIL("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
    {
        OSL_FAIL("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, 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(RTL_CONSTASCII_USTRINGPARAM(""));
    else if (aNameStr.EqualsAscii(SC_UNO_FIELD_SUBTOTALNAME))
        aRet <<= mpSubtotalName.get() ? *mpSubtotalName : OUString(RTL_CONSTASCII_USTRINGPARAM(""));
    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
    {
        OSL_FAIL("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

    //	date columns have 3 hierarchies (flat/quarter/week), other columns only one

    // #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();
}

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 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:
            OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
            break;
    }
    return aRet;
}

void SAL_CALL ScDPHierarchy::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException)
{
    OSL_FAIL("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:
                OSL_FAIL("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();
}

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 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( false ),
    aSortInfo( EMPTY_STRING, sal_True, sheet::DataPilotFieldSortMode::NAME ),   // default: sort by name
    nSortMeasure( 0 ),
    nAutoMeasure( 0 ),
    bEnableLayout( 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();

                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:
                    OSL_FAIL( "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:
                    OSL_FAIL( "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)
{
    OSL_FAIL("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)
{
    SolarMutexGuard 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
    {
        OSL_FAIL("unknown property");
    }
}

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
    {
        OSL_FAIL("unknown property");
    }
    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:
                    {
                        const ScDPItemData* pLastNumData = NULL;
                        for ( SCROW n = 0 ;n <GetSrcItemsCount() ; n-- )
                        {
                            const ScDPItemData* pData  = GetSrcItemDataByIndex( n );
                            if ( pData && pData->HasStringData() )
                                break;
                            else
                                pLastNumData = pData;
                        }

                        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:
                    OSL_FAIL( "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:
                    OSL_FAIL( "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();
}

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!
                    
                    double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
                    long nFirstYear = pSource->GetData()->GetDatePart(
                                        (long)::rtl::math::approxFloor( fFirstVal ),
                                        nHier, nLev );

                    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( 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)
{
    OSL_FAIL("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)
{
    SolarMutexGuard 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
    {
        OSL_FAIL("unknown property");
    }
}

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
    {
        OSL_FAIL("unknown property");
    }
    return aRet;
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )


const ScDPCache* 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();
 }

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
