/*************************************************************************
 *
 * 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_sw.hxx"

#include <memory>
#include <algorithm>

#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/chart2/data/LabelOrigin.hpp>
#include <cppuhelper/interfacecontainer.hxx>
#include <vos/mutex.hxx>
#include <osl/mutex.hxx>
#include <vcl/svapp.hxx>
#include <svl/zforlist.hxx>     // SvNumberFormatter
#include <svtools/chartprettypainter.hxx>

#include <tools/link.hxx>

#include <XMLRangeHelper.hxx>
#include <unochart.hxx>
#include <swtable.hxx>
#include <unoprnms.hxx>
#include <unomap.hxx>
#include <unomid.h>
#include <unocrsr.hxx>
#include <unotbl.hxx>
#include <doc.hxx>
#include <frmfmt.hxx>
#include <docsh.hxx>
#include <ndole.hxx>
#include <swtable.hxx>
#include <swtypes.hxx>
#ifndef _UNOCORE_HRC
#include <unocore.hrc>
#endif

#include <docary.hxx>

#define SN_DATA_PROVIDER            "com.sun.star.chart2.data.DataProvider"
#define SN_DATA_SOURCE              "com.sun.star.chart2.data.DataSource"
#define SN_DATA_SEQUENCE            "com.sun.star.chart2.data.DataSequence"
#define SN_LABELED_DATA_SEQUENCE    "com.sun.star.chart2.data.LabeledDataSequence"

#define DIRECTION_DONT_KNOW     -1
#define DIRECTION_HAS_ERROR     -2
#define DIRECTION_COLS           0
#define DIRECTION_ROWS           1

using namespace ::com::sun::star;
using ::rtl::OUString;

// from unotbl.cxx
extern void lcl_GetCellPosition( const String &rCellName, sal_Int32 &rColumn, sal_Int32 &rRow);
extern String lcl_GetCellName( sal_Int32 nColumn, sal_Int32 nRow );
extern int lcl_CompareCellsByColFirst( const String &rCellName1, const String &rCellName2 );
extern int lcl_CompareCellsByRowFirst( const String &rCellName1, const String &rCellName2 );
extern int lcl_CompareCellRanges(
        const String &rRange1StartCell, const String &rRange1EndCell,
        const String &rRange2StartCell, const String &rRange2EndCell,
        sal_Bool bCmpColsFirst );
extern void lcl_NormalizeRange( String &rCell1, String &rCell2 );

//////////////////////////////////////////////////////////////////////

//static
void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
{
    if (!pDoc)
        return;

    uno::Reference< frame::XModel > xRes;

    SwOLENode *pONd;
    SwStartNode *pStNd;
    SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
    while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
    {
        aIdx++;
        if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
            ChartPrettyPainter::IsChart( pONd->GetOLEObj().GetObject() ) )
        {
            // Load the object and set modified

            uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
            if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
            {
                try
                {
                    uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
                    xModif->setModified( sal_True );
                }
                catch ( uno::Exception& )
                {
                }
                
            }
        }
        aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
    }
}

//////////////////////////////////////////////////////////////////////

SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
    pDoc( pDocument )
{
    aUnlockTimer.SetTimeout( 1500 );
    aUnlockTimer.SetTimeoutHdl( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
}


SwChartLockController_Helper::~SwChartLockController_Helper()
{
    if (pDoc)   // still connected?
        Disconnect();
}


void SwChartLockController_Helper::StartOrContinueLocking()
{
    if (!bIsLocked)
        LockAllCharts();
    aUnlockTimer.Start();   // start or continue time of locking
}


void SwChartLockController_Helper::Disconnect()
{
    aUnlockTimer.Stop();
    UnlockAllCharts();
    pDoc = 0;
}


void SwChartLockController_Helper::LockUnlockAllCharts( sal_Bool bLock )
{
    if (!pDoc)
        return;

    const SwFrmFmts& rTblFmts = *pDoc->GetTblFrmFmts();
    for( sal_uInt16 n = 0; n < rTblFmts.Count(); ++n )
    {
        SwTable* pTmpTbl;
        const SwTableNode* pTblNd;
        SwFrmFmt* pFmt = rTblFmts[ n ];

        if( 0 != ( pTmpTbl = SwTable::FindTable( pFmt ) ) &&
            0 != ( pTblNd = pTmpTbl->GetTableNode() ) &&
            pTblNd->GetNodes().IsDocNodes() )
        {
            uno::Reference< frame::XModel > xRes;

            String aName( pTmpTbl->GetFrmFmt()->GetName() );
            SwOLENode *pONd;
            SwStartNode *pStNd;
            SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
            while( 0 != (pStNd = aIdx.GetNode().GetStartNode()) )
            {
                aIdx++;
                if (0 != ( pONd = aIdx.GetNode().GetOLENode() ) &&
                    pONd->GetChartTblName().Len() > 0 /* is chart object? */)
                {
                    uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
                    if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
                    {
                        xRes = uno::Reference < frame::XModel >( xIP->getComponent(), uno::UNO_QUERY );
                        if (xRes.is())
                        {
                            if (bLock)
                                xRes->lockControllers();
                            else
                                xRes->unlockControllers();
                        }
                    }
                }
                aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
            }
        }
    }

    bIsLocked = bLock;
}


IMPL_LINK( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, /*pTimer*/ )
{
    UnlockAllCharts();
    return 0;
}


//////////////////////////////////////////////////////////////////////

static osl::Mutex &    GetChartMutex()
{
    static osl::Mutex   aMutex;
    return aMutex;
}


static void LaunchModifiedEvent(
		::cppu::OInterfaceContainerHelper &rICH,
		const uno::Reference< uno::XInterface > &rxI )
{
    lang::EventObject aEvtObj( rxI );
    cppu::OInterfaceIteratorHelper aIt( rICH );
    while (aIt.hasMoreElements())
    {
        uno::Reference< util::XModifyListener > xRef( aIt.next(), uno::UNO_QUERY );
        if (xRef.is())
            xRef->modified( aEvtObj );
    }
}

//////////////////////////////////////////////////////////////////////

// rCellRangeName needs to be of one of the following formats:
// - e.g. "A2:E5" or
// - e.g. "Table1.A2:E5"
sal_Bool FillRangeDescriptor(
        SwRangeDescriptor &rDesc,
        const String &rCellRangeName )
{
	xub_StrLen nToken = STRING_NOTFOUND == rCellRangeName.Search('.') ? 0 : 1;
	String aCellRangeNoTableName( rCellRangeName.GetToken( nToken, '.' ) );
    String aTLName( aCellRangeNoTableName.GetToken(0, ':') );  // name of top left cell
    String aBRName( aCellRangeNoTableName.GetToken(1, ':') );  // name of bottom right cell
    if(!aTLName.Len() || !aBRName.Len())
        return sal_False;

    rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
    lcl_GetCellPosition( aTLName, rDesc.nLeft,  rDesc.nTop );
    lcl_GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
    rDesc.Normalize();
    DBG_ASSERT( rDesc.nTop    != -1 &&
                rDesc.nLeft   != -1 &&
                rDesc.nBottom != -1 &&
                rDesc.nRight  != -1,
            "failed to get range descriptor" );
    DBG_ASSERT( rDesc.nTop <= rDesc.nBottom  &&  rDesc.nLeft <= rDesc.nRight,
            "invalid range descriptor");
    return sal_True;
}


static String GetCellRangeName( SwFrmFmt &rTblFmt, SwUnoCrsr &rTblCrsr )
{
    String aRes;

    //!! see also SwXTextTableCursor::getRangeName

    SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(&rTblCrsr);
    if (!pUnoTblCrsr)
        return String();
    pUnoTblCrsr->MakeBoxSels();

    const SwStartNode*  pStart;
    const SwTableBox*   pStartBox   = 0;
    const SwTableBox*   pEndBox     = 0;

    pStart = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
    if (pStart)
    {
        const SwTable* pTable = SwTable::FindTable( &rTblFmt );
        pEndBox = pTable->GetTblBox( pStart->GetIndex());
        aRes = pEndBox->GetName();

        if(pUnoTblCrsr->HasMark())
        {
            pStart = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
            pStartBox = pTable->GetTblBox( pStart->GetIndex());
        }
        DBG_ASSERT( pStartBox, "start box not found" );
        DBG_ASSERT( pEndBox, "end box not found" );
        // need to switch start and end?
        if (*pUnoTblCrsr->GetPoint() < *pUnoTblCrsr->GetMark())
        {
            const SwTableBox* pTmpBox = pStartBox;
            pStartBox = pEndBox;
            pEndBox = pTmpBox;
        }

        aRes = pStartBox->GetName();
        aRes += (sal_Unicode)':';
        if (pEndBox)
            aRes += pEndBox->GetName();
        else
            aRes += pStartBox->GetName();
    }

    return aRes;
}


static String GetRangeRepFromTableAndCells( const String &rTableName,
        const String &rStartCell, const String &rEndCell,
        sal_Bool bForceEndCellName )
{
    DBG_ASSERT( rTableName.Len(), "table name missing" );
    DBG_ASSERT( rStartCell.Len(), "cell name missing" );
    String aRes( rTableName );
    aRes += (sal_Unicode) '.';
    aRes += rStartCell;

    if (rEndCell.Len())
    {
        aRes += (sal_Unicode) ':';
        aRes += rEndCell;
    }
    else if (bForceEndCellName)
    {
        aRes += (sal_Unicode) ':';
        aRes += rStartCell;
    }

    return aRes;
}


static sal_Bool GetTableAndCellsFromRangeRep(
        const OUString &rRangeRepresentation,
        String &rTblName,
        String &rStartCell,
        String &rEndCell,
        sal_Bool bSortStartEndCells = sal_True )
{
    // parse range representation for table name and cell/range names
    // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
    String aTblName;    // table name
    OUString aRange;    // cell range
    String aStartCell;  // name of top left cell
    String aEndCell;    // name of bottom right cell
    sal_Int32 nIdx = rRangeRepresentation.indexOf( '.' );
    if (nIdx >= 0)
    {
        aTblName = rRangeRepresentation.copy( 0, nIdx );
        aRange = rRangeRepresentation.copy( nIdx + 1 );
		sal_Int32 nPos = aRange.indexOf( ':' );
        if (nPos >= 0) // a cell-range like "Table1.A2:D4"
        {
            aStartCell = aRange.copy( 0, nPos );
            aEndCell   = aRange.copy( nPos + 1 );

            // need to switch start and end cell ?
            // (does not check for normalization here)
            if (bSortStartEndCells && 1 == lcl_CompareCellsByColFirst( aStartCell, aEndCell ))
            {
                String aTmp( aStartCell );
                aStartCell  = aEndCell;
                aEndCell    = aTmp;
            }
        }
		else	// a single cell like in "Table1.B3"
		{
			aStartCell = aEndCell = aRange;
		}
    }

    sal_Bool bSuccess = aTblName.Len() != 0 &&
                        aStartCell.Len() != 0 && aEndCell.Len() != 0;
    if (bSuccess)
    {
        rTblName    = aTblName;
        rStartCell  = aStartCell;
        rEndCell    = aEndCell;
    }
    return bSuccess;
}


static void GetTableByName( const SwDoc &rDoc, const String &rTableName,
        SwFrmFmt **ppTblFmt, SwTable **ppTable)
{
    SwFrmFmt *pTblFmt = NULL;

    // find frame format of table
    //! see SwXTextTables::getByName
    sal_uInt16 nCount = rDoc.GetTblFrmFmtCount(sal_True);
    for (sal_uInt16 i = 0; i < nCount && !pTblFmt; ++i)
    {
        SwFrmFmt& rTblFmt = rDoc.GetTblFrmFmt(i, sal_True);
        if(rTableName == rTblFmt.GetName())
            pTblFmt = &rTblFmt;
    }

    if (ppTblFmt)
        *ppTblFmt = pTblFmt;

    if (ppTable)
        *ppTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
}


static void GetFormatAndCreateCursorFromRangeRep(
        const SwDoc    *pDoc,
        const OUString &rRangeRepresentation,   // must be a single range (i.e. so called sub-range)
        SwFrmFmt    **ppTblFmt,     // will be set to the table format of the table used in the range representation
        SwUnoCrsr   **ppUnoCrsr )   // will be set to cursor spanning the cell range
                                    // (cursor will be created!)
{
    String aTblName;    // table name
    String aStartCell;  // name of top left cell
    String aEndCell;    // name of bottom right cell
    sal_Bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
                                  aTblName, aStartCell, aEndCell );

    if (!bNamesFound)
    {
		if (ppTblFmt)
			*ppTblFmt   = NULL;
		if (ppUnoCrsr)
			*ppUnoCrsr  = NULL;
    }
    else
    {
        SwFrmFmt *pTblFmt = NULL;

        // is the correct table format already provided?
        if (*ppTblFmt != NULL  &&  (*ppTblFmt)->GetName() == aTblName)
            pTblFmt = *ppTblFmt;
        else if (ppTblFmt)
            GetTableByName( *pDoc, aTblName, &pTblFmt, NULL );

		if (ppTblFmt)
			*ppTblFmt = pTblFmt;

        if (ppUnoCrsr != NULL)
        {
            *ppUnoCrsr = NULL;  // default result in case of failure

            SwTable *pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
            // create new SwUnoCrsr spanning the specified range
            //! see also SwXTextTable::GetRangeByName
            // --> OD 2007-08-03 #i80314#
            // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
            const SwTableBox* pTLBox =
                            pTable ? pTable->GetTblBox( aStartCell, true ) : 0;
            // <--
            if(pTLBox)
            {
                // hier muessen die Actions aufgehoben werden
                UnoActionRemoveContext aRemoveContext(pTblFmt->GetDoc());
                const SwStartNode* pSttNd = pTLBox->GetSttNd();
                SwPosition aPos(*pSttNd);
                // set cursor to top left box of range
                SwUnoCrsr* pUnoCrsr = pTblFmt->GetDoc()->CreateUnoCrsr(aPos, sal_True);
                pUnoCrsr->Move( fnMoveForward, fnGoNode );
                pUnoCrsr->SetRemainInSection( sal_False );
                // --> OD 2007-08-03 #i80314#
                // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTblBox(..)>
                const SwTableBox* pBRBox = pTable->GetTblBox( aEndCell, true );
                // <--
                if(pBRBox)
                {
                    pUnoCrsr->SetMark();
                    pUnoCrsr->GetPoint()->nNode = *pBRBox->GetSttNd();
                    pUnoCrsr->Move( fnMoveForward, fnGoNode );
                    SwUnoTableCrsr* pCrsr =
                        dynamic_cast<SwUnoTableCrsr*>(pUnoCrsr);
                    pCrsr->MakeBoxSels();

                    if (ppUnoCrsr)
                        *ppUnoCrsr = pCrsr;
                }
                else
                {
                    delete pUnoCrsr;
                }
            }
        }
    }
}


static sal_Bool GetSubranges( const OUString &rRangeRepresentation,
        uno::Sequence< OUString > &rSubRanges, sal_Bool bNormalize )
{
    sal_Bool bRes = sal_True;
    String aRangesStr( rRangeRepresentation );
    xub_StrLen nLen = aRangesStr.GetTokenCount( ';' );
    uno::Sequence< OUString > aRanges( nLen );

    sal_Int32 nCnt = 0;
    if (nLen != 0)
    {
        OUString *pRanges = aRanges.getArray();
        String aFirstTable;
        for ( xub_StrLen i = 0;  i < nLen && bRes;  ++i)
        {
            String aRange( aRangesStr.GetToken( i, ';' ) );
            if (aRange.Len())
            {
                pRanges[nCnt] = aRange;

                String aTableName, aStartCell, aEndCell;
                bRes &= GetTableAndCellsFromRangeRep( aRange,
                                aTableName, aStartCell, aEndCell );

                if (bNormalize)
                {
                    lcl_NormalizeRange( aStartCell, aEndCell );
                    pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
                                    aStartCell, aEndCell, sal_True );
                }

                // make sure to use only a single table
                if (nCnt == 0)
                    aFirstTable = aTableName;
                else
                    bRes &= aFirstTable == aTableName;

                ++nCnt;
            }
        }
    }
    aRanges.realloc( nCnt );

    rSubRanges = aRanges;
    return bRes;
}


static void SortSubranges( uno::Sequence< OUString > &rSubRanges, sal_Bool bCmpByColumn )
{
    sal_Int32 nLen = rSubRanges.getLength();
    OUString *pSubRanges = rSubRanges.getArray();

    String aSmallestTblName;
    String aSmallestStartCell;
    String aSmallestEndCell;

    for (sal_Int32 i = 0;  i < nLen;  ++i)
    {
		sal_Int32 nIdxOfSmallest = i;
		GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
				aSmallestTblName, aSmallestStartCell, aSmallestEndCell );
		if (aSmallestEndCell.Len() == 0)
			aSmallestEndCell = aSmallestStartCell;

        for (sal_Int32 k = i+1;  k < nLen;  ++k)
        {
            // get cell names for sub range
            String aTblName;
            String aStartCell;
            String aEndCell;
            GetTableAndCellsFromRangeRep( pSubRanges[k],
                    aTblName, aStartCell, aEndCell );
            if (aEndCell.Len() == 0)
                aEndCell = aStartCell;

            // compare cell ranges ( is the new one smaller? )
            if (-1 == lcl_CompareCellRanges( aStartCell, aEndCell,
                                aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
            {
                nIdxOfSmallest = k;
                aSmallestTblName    = aTblName;
                aSmallestStartCell  = aStartCell;
                aSmallestEndCell    = aEndCell;
            }
        }

        // move smallest element to the start of the not sorted area
        OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
        pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
        pSubRanges[ i ] = aTmp;
    }
}

//////////////////////////////////////////////////////////////////////

SwChartDataProvider::SwChartDataProvider( const SwDoc* pSwDoc ) :
    aEvtListeners( GetChartMutex() ),
    pDoc( pSwDoc )
{
    bDisposed = sal_False;
}


SwChartDataProvider::~SwChartDataProvider()
{
}

uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
        const uno::Sequence< beans::PropertyValue >& rArguments, sal_Bool bTestOnly )
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Reference< chart2::data::XDataSource > xRes;

    if (!pDoc)
        throw uno::RuntimeException();

    // get arguments
    OUString aRangeRepresentation;
    uno::Sequence< sal_Int32 > aSequenceMapping;
    sal_Bool bFirstIsLabel      = sal_False;
    sal_Bool bDtaSrcIsColumns   = sal_True; // true : DataSource will be sequence of columns
                                            // false: DataSource will be sequence of rows
    OUString aChartOleObjectName;//work around wrong writer ranges ( see Issue 58464 )
    sal_Int32 nArgs = rArguments.getLength();
    DBG_ASSERT( nArgs != 0, "no properties provided" );
    if (nArgs == 0)
        return xRes;
    const beans::PropertyValue *pArg = rArguments.getConstArray();
    for (sal_Int32 i = 0;  i < nArgs;  ++i)
    {
        if (pArg[i].Name.equalsAscii( "DataRowSource" ))
        {
            chart::ChartDataRowSource eSource;
            if (!(pArg[i].Value >>= eSource))
            {
                sal_Int32 nTmp = 0;
                if (!(pArg[i].Value >>= nTmp))
                    throw lang::IllegalArgumentException();
                eSource = static_cast< chart::ChartDataRowSource >( nTmp );
            }
            bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
        }
        else if (pArg[i].Name.equalsAscii( "FirstCellAsLabel" ))
        {
            if (!(pArg[i].Value >>= bFirstIsLabel))
                throw lang::IllegalArgumentException();
        }
        else if (pArg[i].Name.equalsAscii( "CellRangeRepresentation" ))
        {
            if (!(pArg[i].Value >>= aRangeRepresentation))
                throw lang::IllegalArgumentException();
        }
        else if (pArg[i].Name.equalsAscii( "SequenceMapping" ))
        {
            if (!(pArg[i].Value >>= aSequenceMapping))
                throw lang::IllegalArgumentException();
        }
        else if (pArg[i].Name.equalsAscii( "ChartOleObjectName" ))
        {
            if (!(pArg[i].Value >>= aChartOleObjectName))
                throw lang::IllegalArgumentException();
        }
	}

	uno::Sequence< OUString > aSubRanges;
    // get sub-ranges and check that they all are from the very same table
    sal_Bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );

    if (!bOk && pDoc && aChartOleObjectName.getLength() )
    {
        //try to correct the range here
        //work around wrong writer ranges ( see Issue 58464 )
        String aChartTableName;

        const SwNodes& rNodes = pDoc->GetNodes();
        for( sal_uLong nN = rNodes.Count(); nN--; )
        {
            SwNodePtr pNode = rNodes[nN];
            if( !pNode )
                continue;
            const SwOLENode* pOleNode = pNode->GetOLENode();
            if( !pOleNode )
                continue;
            const SwOLEObj& rOObj = pOleNode->GetOLEObj();
            if( aChartOleObjectName.equals( rOObj.GetCurrentPersistName() ) )
            {
                aChartTableName = pOleNode->GetChartTblName();
                break;
            }
        }

        if( aChartTableName.Len() )
        {
            //the wrong range is still shifted one row down
            //thus the first row is missing and an invalid row at the end is added.
            //Therefore we need to shift the range one row up
            SwRangeDescriptor aDesc;
            if (aRangeRepresentation.getLength() == 0)
                return xRes;        // we cant handle this thus returning an empty references
            aRangeRepresentation = aRangeRepresentation.copy( 1 );    // get rid of '.' to have only the cell range left
            FillRangeDescriptor( aDesc, aRangeRepresentation );
            aDesc.Normalize();
            if (aDesc.nTop <= 0)    // no chance to shift the range one row up?
                return xRes;        // we cant handle this thus returning an empty references
            aDesc.nTop      -= 1;
            aDesc.nBottom   -= 1;

            String aNewStartCell( lcl_GetCellName( aDesc.nLeft, aDesc.nTop ) );
            String aNewEndCell( lcl_GetCellName( aDesc.nRight, aDesc.nBottom ) );
            aRangeRepresentation = GetRangeRepFromTableAndCells(
                        aChartTableName, aNewStartCell, aNewEndCell, sal_True );
            bOk = GetSubranges( aRangeRepresentation, aSubRanges, sal_True );
        }
    }
    if (!bOk)    // different tables used, or incorrect range specifiers
        throw lang::IllegalArgumentException();

    SortSubranges( aSubRanges, bDtaSrcIsColumns );
    const OUString *pSubRanges = aSubRanges.getConstArray();
#if OSL_DEBUG_LEVEL > 1
    {
        sal_Int32 nSR = aSubRanges.getLength();
        OUString *pSR = aSubRanges.getArray();
        OUString aRg;
        for (sal_Int32 i = 0;  i < nSR;  ++i)
        {
            aRg = pSR[i];
        }
    }
#endif

    // get table format for that single table from above
    SwFrmFmt    *pTblFmt  = 0;      // pointer to table format
    SwUnoCrsr   *pUnoCrsr = 0;      // here required to check if the cells in the range do actually exist
    std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr );  // to end lifetime of object pointed to by pUnoCrsr
    if (aSubRanges.getLength() > 0)
        GetFormatAndCreateCursorFromRangeRep( pDoc, pSubRanges[0], &pTblFmt, &pUnoCrsr );
    if (!pTblFmt || !pUnoCrsr)
        throw lang::IllegalArgumentException();

    if(pTblFmt)
    {
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if(pTable->IsTblComplex())
            return xRes;    // we cant handle this thus returning an empty references
        else
        {
            // get a character map in the size of the table to mark
            // all the ranges to use in
            sal_Int32 nRows = pTable->GetTabLines().Count();
            sal_Int32 nCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
            std::vector< std::vector< sal_Char > > aMap( nRows );
            for (sal_Int32 i = 0;  i < nRows;  ++i)
                aMap[i].resize( nCols );

            // iterate over subranges and mark used cells in above map
            //!! by proceeding this way we automatically get rid of
            //!! multiple listed or overlapping cell ranges which should
            //!! just be ignored silently
            sal_Int32 nSubRanges = aSubRanges.getLength();
            for (sal_Int32 i = 0;  i < nSubRanges;  ++i)
            {
                String aTblName, aStartCell, aEndCell;
                sal_Bool bOk2 = GetTableAndCellsFromRangeRep(
                                    pSubRanges[i], aTblName, aStartCell, aEndCell );
                (void) bOk2;
                DBG_ASSERT( bOk2, "failed to get table and start/end cells" );

                sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
                lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
                lcl_GetCellPosition( aEndCell,   nEndCol,   nEndRow );
                DBG_ASSERT( nStartRow <= nEndRow && nStartCol <= nEndCol,
                        "cell range not normalized");

                // test if the ranges span more than the available cells
                if( nStartRow < 0 || nEndRow >= nRows ||
                    nStartCol < 0 || nEndCol >= nCols )
                {
                    throw lang::IllegalArgumentException();
                }
                for (sal_Int32 k1 = nStartRow;  k1 <= nEndRow;  ++k1)
                {
                    for (sal_Int32 k2 = nStartCol;  k2 <= nEndCol;  ++k2)
                        aMap[k1][k2] = 'x';
                }
            }

            //
            // find label and data sequences to use
            //
            sal_Int32 oi;  // outer index (slower changing index)
            sal_Int32 ii;  // inner index (faster changing index)
            sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
            sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
            std::vector< sal_Int32 > aLabelIdx( oiEnd );
            std::vector< sal_Int32 > aDataStartIdx( oiEnd );
            std::vector< sal_Int32 > aDataLen( oiEnd );
            for (oi = 0;  oi < oiEnd;  ++oi)
            {
                aLabelIdx[oi]       = -1;
                aDataStartIdx[oi]   = -1;
                aDataLen[oi]        = 0;
            }
            //
            for (oi = 0;  oi < oiEnd;  ++oi)
            {
                ii = 0;
                while (ii < iiEnd)
                {
                    sal_Char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];

                    // label should be used but is not yet found?
                    if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
                    {
                        aLabelIdx[oi] = ii;
                        rChar = 'L';    // setting a different char for labels here
                                        // makes the test for the data sequence below
                                        // easier
                    }

                    // find data sequence
                    if (rChar == 'x' && aDataStartIdx[oi] == -1)
                    {
                        aDataStartIdx[oi] = ii;

                        // get length of data sequence
                        sal_Int32 nL = 0;
                        sal_Char c;
                        while (ii< iiEnd && 'x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
                        {
                            ++nL;   ++ii;
                        }
                        aDataLen[oi] = nL;

                        // check that there is no other seperate sequence of data
                        // to be found because that is not supported
                        while (ii < iiEnd)
                        {
                            if ('x' == (c = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
                                throw lang::IllegalArgumentException();
                            ++ii;
                        }
                    }
                    else
                        ++ii;
                }
            }

            // make some other consistency checks while calculating
            // the number of XLabeledDataSequence to build:
            // - labels should always be used or not at all
            // - the data sequences should have equal non-zero length
            sal_Int32 nNumLDS = 0;
            if (oiEnd > 0)
            {
                sal_Int32 nFirstSeqLen = 0;
                sal_Int32 nFirstSeqLabelIdx = -1;
                for (oi = 0;  oi < oiEnd;  ++oi)
                {
                    sal_Bool bFirstFound = sal_False;
                    // row/col used at all?
                    if (aDataStartIdx[oi] != -1 &&
                        (!bFirstIsLabel || aLabelIdx[oi] != -1))
                    {
                        ++nNumLDS;
                        if (!bFirstFound)
                        {
                            nFirstSeqLen        = aDataLen[oi];
                            nFirstSeqLabelIdx   = aLabelIdx[oi];
                            bFirstFound = sal_True;
                        }
                        else
                        {
                            if (nFirstSeqLen != aDataLen[oi] ||
                                nFirstSeqLabelIdx != aLabelIdx[oi])
                                throw lang::IllegalArgumentException();
                        }
                    }
                }
            }
            if (nNumLDS == 0)
                throw lang::IllegalArgumentException();

            // now we should have all necessary data to build a proper DataSource
            // thus if we came this far there should be no further problem
            if (bTestOnly)
                return xRes;    // have createDataSourcePossible return true

            // create data source from found label and data sequences
            uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aLabelSeqs( nNumLDS );
            uno::Reference< chart2::data::XDataSequence > *pLabelSeqs = aLabelSeqs.getArray();
            uno::Sequence< uno::Reference< chart2::data::XDataSequence > > aDataSeqs( nNumLDS );
            uno::Reference< chart2::data::XDataSequence > *pDataSeqs = aDataSeqs.getArray();
            sal_Int32 nSeqsIdx = 0;
            for (oi = 0;  oi < oiEnd;  ++oi)
            {
                // row/col not used? (see if-statement above where nNumLDS was counted)
                if (!(aDataStartIdx[oi] != -1 &&
                        (!bFirstIsLabel || aLabelIdx[oi] != -1)))
                    continue;

                // get cell ranges for label and data
                //
                SwRangeDescriptor aLabelDesc;
                SwRangeDescriptor aDataDesc;
                if (bDtaSrcIsColumns)   // use columns
                {
                    aLabelDesc.nTop     = aLabelIdx[oi];
                    aLabelDesc.nLeft    = oi;
                    aLabelDesc.nBottom  = aLabelDesc.nTop;
                    aLabelDesc.nRight   = oi;

                    aDataDesc.nTop      = aDataStartIdx[oi];
                    aDataDesc.nLeft     = oi;
                    aDataDesc.nBottom   = aDataDesc.nTop + aDataLen[oi] - 1;
                    aDataDesc.nRight    = oi;
                }
                else    // use rows
                {
                    aLabelDesc.nTop     = oi;
                    aLabelDesc.nLeft    = aLabelIdx[oi];
                    aLabelDesc.nBottom  = oi;
                    aLabelDesc.nRight   = aLabelDesc.nLeft;

                    aDataDesc.nTop      = oi;
                    aDataDesc.nLeft     = aDataStartIdx[oi];
                    aDataDesc.nBottom   = oi;
                    aDataDesc.nRight    = aDataDesc.nLeft + aDataLen[oi] - 1;
                }
                String aBaseName( pTblFmt->GetName() );
                aBaseName += '.';
                //
                String aLabelRange;
                if (aLabelIdx[oi] != -1)
                {
                    aLabelRange += aBaseName;
                    aLabelRange += lcl_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop );
                    aLabelRange += ':';
                    aLabelRange += lcl_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
                }
                //
                String aDataRange;
                if (aDataStartIdx[oi] != -1)
                {
                    aDataRange += aBaseName;
                    aDataRange += lcl_GetCellName( aDataDesc.nLeft, aDataDesc.nTop );
                    aDataRange += ':';
                    aDataRange += lcl_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
                }

                // get cursors spanning the cell ranges for label and data
                SwUnoCrsr   *pLabelUnoCrsr  = 0;
                SwUnoCrsr   *pDataUnoCrsr   = 0;
                GetFormatAndCreateCursorFromRangeRep( pDoc, aLabelRange, &pTblFmt, &pLabelUnoCrsr);
                GetFormatAndCreateCursorFromRangeRep( pDoc, aDataRange,  &pTblFmt, &pDataUnoCrsr);

                // create XDataSequence's from cursors
				if (pLabelUnoCrsr)
                    pLabelSeqs[ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pLabelUnoCrsr );
                DBG_ASSERT( pDataUnoCrsr, "pointer to data sequence missing" );
				if (pDataUnoCrsr)
                    pDataSeqs [ nSeqsIdx ] = new SwChartDataSequence( *this, *pTblFmt, pDataUnoCrsr );
                if (pLabelUnoCrsr || pDataUnoCrsr)
                    ++nSeqsIdx;
            }
            DBG_ASSERT( nSeqsIdx == nNumLDS,
                    "mismatch between sequence size and num,ber of entries" );

            // build data source from data and label sequences
            uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLDS( nNumLDS );
            uno::Reference< chart2::data::XLabeledDataSequence > *pLDS = aLDS.getArray();
            for (sal_Int32 i = 0;  i < nNumLDS;  ++i)
            {
                SwChartLabeledDataSequence *pLabeledDtaSeq = new SwChartLabeledDataSequence;
                pLabeledDtaSeq->setLabel( pLabelSeqs[i] );
                pLabeledDtaSeq->setValues( pDataSeqs[i] );
                pLDS[i] = pLabeledDtaSeq;
            }

            // apply 'SequenceMapping' if it was provided
            sal_Int32 nSequenceMappingLen = aSequenceMapping.getLength();
            if (nSequenceMappingLen)
            {
                sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
                uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aOld_LDS( aLDS );
                uno::Reference< chart2::data::XLabeledDataSequence > *pOld_LDS = aOld_LDS.getArray();

                sal_Int32 nNewCnt = 0;
                for (sal_Int32 i = 0;  i < nSequenceMappingLen;  ++i)
                {
                    // check that index to be used is valid
                    // and has not yet been used
                    sal_Int32 nIdx = pSequenceMapping[i];
                    if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
                    {
                        pLDS[nNewCnt++] = pOld_LDS[nIdx];

                        // mark index as being used already (avoids duplicate entries)
                        pOld_LDS[nIdx].clear();
                    }
                }
                // add not yet used 'old' sequences to new one
                for (sal_Int32 i = 0;  i < nNumLDS;  ++i)
                {
#if OSL_DEBUG_LEVEL > 1
                        if (!pOld_LDS[i].is())
                            i = i;
#endif
                    if (pOld_LDS[i].is())
                        pLDS[nNewCnt++] = pOld_LDS[i];
                }
                DBG_ASSERT( nNewCnt == nNumLDS, "unexpected size of resulting sequence" );
            }

            xRes = new SwChartDataSource( aLDS );
        }
    }

    return xRes;
}

sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
        const uno::Sequence< beans::PropertyValue >& rArguments )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    sal_Bool bPossible = sal_True;
    try
    {
        Impl_createDataSource( rArguments, sal_True );
    }
    catch (lang::IllegalArgumentException &)
    {
        bPossible = sal_False;
    }

    return bPossible;
}

uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
        const uno::Sequence< beans::PropertyValue >& rArguments )
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return Impl_createDataSource( rArguments );
}

////////////////////////////////////////////////////////////
// SwChartDataProvider::GetBrokenCellRangeForExport
//
// fix for #i79009
// we need to return a property that has the same value as the property
// 'CellRangeRepresentation' but for all rows which are increased by one.
// E.g. Table1:A1:D5 -> Table1:A2:D6
// Since the problem is only for old charts which did not support multiple
// we do not need to provide that property/string if the 'CellRangeRepresentation'
// contains multiple ranges.
OUString SwChartDataProvider::GetBrokenCellRangeForExport(
    const OUString &rCellRangeRepresentation )
{
    OUString aRes;

    // check that we do not have multiple ranges
    if (-1 == rCellRangeRepresentation.indexOf( ';' ))
    {
        // get current cell and table names
        String aTblName, aStartCell, aEndCell;
        GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
            aTblName, aStartCell, aEndCell, sal_False );
        sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
        lcl_GetCellPosition( aStartCell, nStartCol, nStartRow );
        lcl_GetCellPosition( aEndCell, nEndCol, nEndRow );

        // get new cell names
        ++nStartRow;
        ++nEndRow;
        aStartCell = lcl_GetCellName( nStartCol, nStartRow );
        aEndCell   = lcl_GetCellName( nEndCol, nEndRow );

        aRes = GetRangeRepFromTableAndCells( aTblName,
                aStartCell, aEndCell, sal_False );
    }

    return aRes;
}

uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
        const uno::Reference< chart2::data::XDataSource >& xDataSource )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Sequence< beans::PropertyValue > aResult;
    if (!xDataSource.is())
        return aResult;

    const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
    const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
    sal_Int32 nNumDS_LDS = aDS_LDS.getLength();

    if (nNumDS_LDS == 0)
	{
	    DBG_WARNING( "XLabeledDataSequence in data source contains 0 entries" );
        return aResult;
	}

    SwFrmFmt *pTableFmt = 0;
    SwTable  *pTable    = 0;
    String    aTableName;
    sal_Int32 nTableRows = 0;
    sal_Int32 nTableCols = 0;

    // data used to build 'CellRangeRepresentation' from later on
    std::vector< std::vector< sal_Char > > aMap;

    uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
    sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();

    String aCellRanges;
    sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true  -2: neither
    sal_Int32 nLabelSeqLen  = -1;   // used to see if labels are always used or not and have
                                    // the expected size of 1 (i.e. if FirstCellAsLabel can
                                    // be determined)
                                    // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
									// -2: neither/failed
//     sal_Int32 nValuesSeqLen = -1;   // used to see if all value sequences have the same size
    for (sal_Int32 nDS1 = 0;  nDS1 < nNumDS_LDS;  ++nDS1)
    {
        uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
        if( !xLabeledDataSequence.is() )
        {
            DBG_ERROR("got NULL for XLabeledDataSequence from Data source");
            continue;
        }
        const uno::Reference< chart2::data::XDataSequence > xCurLabel( xLabeledDataSequence->getLabel(), uno::UNO_QUERY );
        const uno::Reference< chart2::data::XDataSequence > xCurValues( xLabeledDataSequence->getValues(), uno::UNO_QUERY );

        // get sequence lengths for label and values.
		// (0 length is Ok)
        sal_Int32 nCurLabelSeqLen   = -1;
        sal_Int32 nCurValuesSeqLen  = -1;
        if (xCurLabel.is())
            nCurLabelSeqLen = xCurLabel->getData().getLength();
        if (xCurValues.is())
            nCurValuesSeqLen = xCurValues->getData().getLength();

		// check for consistent use of 'first cell as label'
		if (nLabelSeqLen == -1)		// set initial value to compare with below further on
			nLabelSeqLen = nCurLabelSeqLen;
		if (nLabelSeqLen != nCurLabelSeqLen)
			nLabelSeqLen = -2;	// failed / no consistent use of label cells

        // get table and cell names for label and values data sequences
        // (start and end cell will be sorted, i.e. start cell <= end cell)
        String aLabelTblName, aLabelStartCell, aLabelEndCell;
        String aValuesTblName, aValuesStartCell, aValuesEndCell;
        String aLabelRange, aValuesRange;
		if (xCurLabel.is())
			aLabelRange = xCurLabel->getSourceRangeRepresentation();
		if (xCurValues.is())
			aValuesRange = xCurValues->getSourceRangeRepresentation();
        if ((aLabelRange.Len() && !GetTableAndCellsFromRangeRep( aLabelRange,
                aLabelTblName, aLabelStartCell, aLabelEndCell ))  ||
            !GetTableAndCellsFromRangeRep( aValuesRange,
                aValuesTblName, aValuesStartCell, aValuesEndCell ))
        {
            return aResult; // failed -> return empty property sequence
        }

        // make sure all sequences use the same table
        if (!aTableName.Len())
            aTableName = aValuesTblName;  // get initial value to compare with
        if (!aTableName.Len() ||
             aTableName != aValuesTblName ||
            (aLabelTblName.Len() && aTableName != aLabelTblName))
        {
            return aResult; // failed -> return empty property sequence
        }


        // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
        // first and last cell used in both sequences
		//
        sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
        String aCell( aLabelStartCell.Len() ? aLabelStartCell : aValuesStartCell );
        DBG_ASSERT( aCell.Len() , "start cell missing?" );
        lcl_GetCellPosition( aCell, nFirstCol, nFirstRow);
        lcl_GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
        //
        sal_Int16 nDirection = -1;  // -1: not yet set,  0: columns,  1: rows, -2: failed
        if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
        {
            DBG_ASSERT( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
                    "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
            nDirection = 0;     // default direction for a single cell should be 'columns'
        }
        else    // more than one cell is availabale (in values and label together!)
        {
            if (nFirstCol == nLastCol && nFirstRow != nLastRow)
                nDirection = 1;
            else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
                nDirection = 0;
            else
            {
                DBG_ERROR( "trying to determine 'DataRowSource': unexpected case found" );
                nDirection = -2;
            }
        }
        // check for consistent direction of data source
        if (nDtaSrcIsColumns == -1)     // set initial value to compare with below
            nDtaSrcIsColumns = nDirection;
        if (nDtaSrcIsColumns != nDirection)
        {
            nDtaSrcIsColumns = -2;	// failed
        }


		if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
		{
			// build data to obtain 'SequenceMapping' later on
			//
			DBG_ASSERT( nDtaSrcIsColumns == 0  ||   /* rows */
						nDtaSrcIsColumns == 1,      /* columns */
					"unexpected value for 'nDtaSrcIsColumns'" );
			pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;


			// build data used to determine 'CellRangeRepresentation' later on
			//
			GetTableByName( *pDoc, aTableName, &pTableFmt, &pTable );
			if (!pTable || pTable->IsTblComplex())
				return aResult; // failed -> return empty property sequence
			nTableRows = pTable->GetTabLines().Count();
			nTableCols = pTable->GetTabLines().GetObject(0)->GetTabBoxes().Count();
			aMap.resize( nTableRows );
            for (sal_Int32 i = 0;  i < nTableRows;  ++i)
				aMap[i].resize( nTableCols );
			//
			if (aLabelStartCell.Len() && aLabelEndCell.Len())
			{
				sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
				lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
				lcl_GetCellPosition( aLabelEndCell,   nEndCol,   nEndRow );
				if (nStartRow < 0 || nEndRow >= nTableRows ||
					nStartCol < 0 || nEndCol >= nTableCols)
				{
					return aResult; // failed -> return empty property sequence
				}
                for (sal_Int32 i = nStartRow;  i <= nEndRow;  ++i)
                {
                    for (sal_Int32 k = nStartCol;  k <= nEndCol;  ++k)
                    {
                        sal_Char &rChar = aMap[i][k];
                        if (rChar == '\0')   // check for overlapping values and/or labels
                            rChar = 'L';
                        else
                            return aResult; // failed -> return empty property sequence
                    }
                }
			}
			if (aValuesStartCell.Len() && aValuesEndCell.Len())
			{
				sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
				lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
				lcl_GetCellPosition( aValuesEndCell,   nEndCol,   nEndRow );
				if (nStartRow < 0 || nEndRow >= nTableRows ||
					nStartCol < 0 || nEndCol >= nTableCols)
				{
					return aResult; // failed -> return empty property sequence
				}
                for (sal_Int32 i = nStartRow;  i <= nEndRow;  ++i)
                {
                    for (sal_Int32 k = nStartCol;  k <= nEndCol;  ++k)
                    {
                        sal_Char &rChar = aMap[i][k];
                        if (rChar == '\0')   // check for overlapping values and/or labels
                            rChar = 'x';
                        else
                            return aResult; // failed -> return empty property sequence
                    }
                }
			}
		}

#if OSL_DEBUG_LEVEL > 1
        // do some extra sanity checking that the length of the sequences
        // matches their range representation
        {
            sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
            if (xCurLabel.is())
            {
                lcl_GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
                lcl_GetCellPosition( aLabelEndCell,   nEndCol,   nEndRow);
                DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
                            (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
                        "label sequence length does not match range representation!" );
            }
            if (xCurValues.is())
            {
                lcl_GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
                lcl_GetCellPosition( aValuesEndCell,   nEndCol,   nEndRow);
                DBG_ASSERT( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
                            (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
                        "value sequence length does not match range representation!" );
            }
        }
#endif
    } // for


    // build value for 'CellRangeRepresentation'
    //
    String aCellRangeBase( aTableName );
    aCellRangeBase += '.';
    String aCurRange;
    for (sal_Int32 i = 0;  i < nTableRows;  ++i)
	{
        for (sal_Int32 k = 0;  k < nTableCols;  ++k)
        {
            if (aMap[i][k] != '\0')  // top-left cell of a sub-range found
            {
                // find rectangular sub-range to use
                sal_Int32 nRowIndex1 = i;   // row index
                sal_Int32 nColIndex1 = k;   // column index
                sal_Int32 nRowSubLen = 0;
                sal_Int32 nColSubLen = 0;
                while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
                    ++nRowSubLen;
                // be aware of shifted sequences!
                // (according to the checks done prior the length should be ok)
                while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
                                       && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
                {
                    ++nColIndex1;
                    ++nColSubLen;
                }
                String aStartCell( lcl_GetCellName( k, i ) );
                String aEndCell( lcl_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
                aCurRange = aCellRangeBase;
                aCurRange += aStartCell;
                aCurRange += ':';
                aCurRange += aEndCell;
                if (aCellRanges.Len())
                    aCellRanges += ';';
                aCellRanges += aCurRange;

                // clear already found sub-range from map
                for (sal_Int32 nRowIndex2 = 0;  nRowIndex2 < nRowSubLen;  ++nRowIndex2)
                    for (sal_Int32 nColumnIndex2 = 0;  nColumnIndex2 < nColSubLen;  ++nColumnIndex2)
                        aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
            }
        }
    }
    // to be nice to the user we now sort the cell ranges according to
    // rows or columns depending on the direction used in the data source
    uno::Sequence< OUString > aSortedRanges;
    GetSubranges( aCellRanges, aSortedRanges, sal_False /*sub ranges should already be normalized*/ );
    SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
    sal_Int32 nSortedRanges = aSortedRanges.getLength();
    const OUString *pSortedRanges = aSortedRanges.getConstArray();
    OUString aSortedCellRanges;
    for (sal_Int32 i = 0;  i < nSortedRanges;  ++i)
    {
        if (aSortedCellRanges.getLength())
            aSortedCellRanges += OUString::valueOf( (sal_Unicode) ';');
        aSortedCellRanges += pSortedRanges[i];
    }


    // build value for 'SequenceMapping'
    //
    uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
    sal_Int32 *pSortedMapping = aSortedMapping.getArray();
    std::sort( pSortedMapping, pSortedMapping + aSortedMapping.getLength() );
    DBG_ASSERT( aSortedMapping.getLength() == nNumDS_LDS, "unexpected size of sequence" );
	sal_Bool bNeedSequenceMapping = sal_False;
    for (sal_Int32 i = 0;  i < nNumDS_LDS;  ++i)
    {
        sal_Int32 *pIt = std::find( pSortedMapping, pSortedMapping + nNumDS_LDS,
                                    pSequenceMapping[i] );
        DBG_ASSERT( pIt, "index not found" );
        if (!pIt)
            return aResult; // failed -> return empty property sequence
        pSequenceMapping[i] = pIt - pSortedMapping;
		
		if (i != pSequenceMapping[i])
			bNeedSequenceMapping = sal_True;
    }

	// check if 'SequenceMapping' is actually not required...
	// (don't write unnecessary properties to the XML file)
	if (!bNeedSequenceMapping)
		aSequenceMapping.realloc(0);


#ifdef TL_NOT_USED  // in the end chart2 did not want to have the sequence minimized
    // try to shorten the 'SequenceMapping' as much as possible
    sal_Int32 k;
    for (k = nNumDS_LDS - 1;  k >= 0;  --k)
    {
        if (pSequenceMapping[k] != k)
            break;
    }
    aSequenceMapping.realloc( k + 1 );
#endif


    //
    // build resulting properties
    //
    DBG_ASSERT(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
            "unexpected value for 'nLabelSeqLen'" );
    sal_Bool bFirstCellIsLabel = sal_False;     // default value if 'nLabelSeqLen' could not properly determined
    if (nLabelSeqLen > 0) // == 0 means no label sequence in use
        bFirstCellIsLabel = sal_True;
	//
    DBG_ASSERT( aSortedCellRanges.getLength(), "CellRangeRepresentation missing" );
    OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
	//
    aResult.realloc(5);
    sal_Int32 nProps = 0;
    aResult[nProps  ].Name = C2U("FirstCellAsLabel");
    aResult[nProps++].Value <<= bFirstCellIsLabel;
    aResult[nProps  ].Name = C2U("CellRangeRepresentation");
    aResult[nProps++].Value <<= aSortedCellRanges;
    if (0 != aBrokenCellRangeForExport.getLength())
    {
        aResult[nProps  ].Name = C2U("BrokenCellRangeForExport");
        aResult[nProps++].Value <<= aBrokenCellRangeForExport;
    }
	if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
	{
		chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
					chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
		aResult[nProps  ].Name = C2U("DataRowSource");
		aResult[nProps++].Value <<= eDataRowSource;

		if (aSequenceMapping.getLength() != 0)
		{
			aResult[nProps  ].Name = C2U("SequenceMapping");
			aResult[nProps++].Value <<= aSequenceMapping;
		}
	}
	aResult.realloc( nProps );

    return aResult;
}

uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
        const OUString& rRangeRepresentation, sal_Bool bTestOnly )
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    if (bDisposed)
        throw lang::DisposedException();

    SwFrmFmt    *pTblFmt    = 0;    // pointer to table format
    SwUnoCrsr   *pUnoCrsr   = 0;    // pointer to new created cursor spanning the cell range
    GetFormatAndCreateCursorFromRangeRep( pDoc, rRangeRepresentation,
                                          &pTblFmt, &pUnoCrsr );
    if (!pTblFmt || !pUnoCrsr)
        throw lang::IllegalArgumentException();

    // check that cursors point and mark are in a single row or column.
    String aCellRange( GetCellRangeName( *pTblFmt, *pUnoCrsr ) );
    SwRangeDescriptor aDesc;
    FillRangeDescriptor( aDesc, aCellRange );
    if (aDesc.nTop != aDesc.nBottom  &&  aDesc.nLeft != aDesc.nRight)
        throw lang::IllegalArgumentException();

    DBG_ASSERT( pTblFmt && pUnoCrsr, "table format or cursor missing" );
    uno::Reference< chart2::data::XDataSequence > xDataSeq;
    if (!bTestOnly)
        xDataSeq = new SwChartDataSequence( *this, *pTblFmt, pUnoCrsr );

    return xDataSeq;
}

sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
        const OUString& rRangeRepresentation )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    sal_Bool bPossible = sal_True;
    try
    {
        Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, sal_True );
    }
    catch (lang::IllegalArgumentException &)
    {
        bPossible = sal_False;
    }

    return bPossible;
}

uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
        const OUString& rRangeRepresentation )
    throw (lang::IllegalArgumentException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
}


uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection(  )
    throw (uno::RuntimeException)
{
    // note: it is no error to return nothing here
    return uno::Reference< sheet::XRangeSelection >();
}


void SAL_CALL SwChartDataProvider::dispose(  )
    throw (uno::RuntimeException)
{
    sal_Bool bMustDispose( sal_False );
	{
		osl::MutexGuard  aGuard( GetChartMutex() );
        bMustDispose = !bDisposed;
		if (!bDisposed)
			bDisposed = sal_True;
	}
    if (bMustDispose)
    {
        // dispose all data-sequences
        Map_Set_DataSequenceRef_t::iterator aIt( aDataSequences.begin() );
        while (aIt != aDataSequences.end())
		{
            DisposeAllDataSequences( (*aIt).first );
			++aIt;
		}
		// release all references to data-sequences
		aDataSequences.clear();

		// require listeners to release references to this object
        lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
        aEvtListeners.disposeAndClear( aEvtObj );
    }
}


void SAL_CALL SwChartDataProvider::addEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.addInterface( rxListener );
}


void SAL_CALL SwChartDataProvider::removeEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.removeInterface( rxListener );
}



OUString SAL_CALL SwChartDataProvider::getImplementationName(  )
    throw (uno::RuntimeException)
{
    return C2U("SwChartDataProvider");
}


sal_Bool SAL_CALL SwChartDataProvider::supportsService(
        const OUString& rServiceName )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return rServiceName.equalsAscii( SN_DATA_PROVIDER );
}


uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    uno::Sequence< OUString > aRes(1);
    aRes.getArray()[0] = C2U( SN_DATA_PROVIDER );
    return aRes;
}


void SwChartDataProvider::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
{
    // actually this function should be superfluous (need to check later)
    ClientModify(this, pOld, pNew );
}


void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
{
    aDataSequences[ &rTable ].insert( rxDataSequence );
}


void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > &rxDataSequence )
{
    aDataSequences[ &rTable ].erase( rxDataSequence );
}


void SwChartDataProvider::InvalidateTable( const SwTable *pTable )
{
    DBG_ASSERT( pTable, "table pointer is NULL" );
    if (pTable)
    {
		if (!bDisposed)
	       pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();

		const Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];
        Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
        while (aIt != rSet.end())
        {
//            uno::Reference< util::XModifiable > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
            uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
            uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
            if (xRef.is())
            {
                // mark the sequence as 'dirty' and notify listeners
                xRef->setModified( sal_True );
            }
            ++aIt;
        }
    }
}


sal_Bool SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
{
    sal_Bool bRes = sal_False;
    DBG_ASSERT( pTable, "table pointer is NULL" );
    if (pTable)
    {
		if (!bDisposed)
	        pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();

        Set_DataSequenceRef_t &rSet = aDataSequences[ pTable ];

        // iterate over all data-sequences for that table...
        Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
        Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
        Set_DataSequenceRef_t::iterator aDelIt;     // iterator used for deletion when appropriate
        while (aIt != aEndIt)
        {
			SwChartDataSequence *pDataSeq = 0;
            sal_Bool bNowEmpty = sal_False;

            // check if weak reference is still valid...
//            uno::Reference< chart2::data::XDataSequence > xRef( uno::Reference< chart2::data::XDataSequence>(*aIt), uno::UNO_QUERY );
            uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
            uno::Reference< chart2::data::XDataSequence > xRef( xTemp, uno::UNO_QUERY );
            if (xRef.is())
            {
                // then delete that table box (check if implementation cursor needs to be adjusted)
                pDataSeq = static_cast< SwChartDataSequence * >( xRef.get() );
                if (pDataSeq)
                {
#if OSL_DEBUG_LEVEL > 1
                    OUString aRangeStr( pDataSeq->getSourceRangeRepresentation() );
#endif
                    bNowEmpty = pDataSeq->DeleteBox( rBox );
                    if (bNowEmpty)
                        aDelIt = aIt;
                }
            }
            ++aIt;

            if (bNowEmpty)
			{
                rSet.erase( aDelIt );
				if (pDataSeq)
					pDataSeq->dispose();    // the current way to tell chart that sth. got removed
			}
        }
    }
    return bRes;
}


void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
{
    DBG_ASSERT( pTable, "table pointer is NULL" );
    if (pTable)
    {
		if (!bDisposed)
			pTable->GetFrmFmt()->GetDoc()->GetChartControllerHelper().StartOrContinueLocking();

        //! make a copy of the STL container! 
        //! This is necessary since calling 'dispose' will implicitly remove an element 
        //! of the original container, and thus any iterator in the original container
        //! would become invalid.
        const Set_DataSequenceRef_t aSet( aDataSequences[ pTable ] );

        Set_DataSequenceRef_t::iterator aIt( aSet.begin() );
        Set_DataSequenceRef_t::iterator aEndIt( aSet.end() );
        while (aIt != aEndIt)
        {
//            uno::Reference< lang::XComponent > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
            uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
            uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
            if (xRef.is())
            {
                xRef->dispose();
            }
            ++aIt;
        }
    }
}


////////////////////////////////////////
// SwChartDataProvider::AddRowCols tries to notify charts of added columns
// or rows and extends the value sequence respectively (if possible).
// If those can be added to the end of existing value data-sequences those
// sequences get mofdified accordingly and will send a modification
// notification (calling 'setModified').
//
// Since this function is a work-around for non existent Writer core functionality
// (no arbitrary multi-selection in tables that can be used to define a
// data-sequence) this function will be somewhat unreliable.
// For example we will only try to adapt value sequences. For this we assume
// that a sequence of length 1 is a label sequence and those with length >= 2
// we presume to be value sequences. Also new cells can only be added in the
// direction the value sequence is already pointing (rows / cols) and at the
// start or end of the values data-sequence.
// Nothing needs to be done if the new cells are in between the table cursors
// point and mark since data-sequence are considered to consist of all cells
// between those.
// New rows/cols need to be added already to the table before calling
// this function.
//
void SwChartDataProvider::AddRowCols(
        const SwTable &rTable,
        const SwSelBoxes& rBoxes,
        sal_uInt16 nLines, sal_Bool bBehind )
{
	if (rTable.IsTblComplex())
		return;

	const sal_uInt16 nBoxes		= rBoxes.Count();
    if (nBoxes < 1 || nLines < 1)
        return;

	SwTableBox* pFirstBox	= *( rBoxes.GetData() + 0 );
	SwTableBox* pLastBox	= *( rBoxes.GetData() + nBoxes - 1 );

    sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
	if (pFirstBox && pLastBox)
	{
        lcl_GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow  );
        lcl_GetCellPosition( pLastBox->GetName(),  nLastCol,  nLastRow );

        bool bAddCols = false;  // default; also to be used if nBoxes == 1 :-/
        if (nFirstCol == nLastCol && nFirstRow != nLastRow)
            bAddCols = true;
        if (nFirstCol == nLastCol || nFirstRow == nLastRow)
		{
			//get range of indices in col/rows for new cells
            sal_Int32 nFirstNewCol = nFirstCol;
            sal_Int32 nLastNewCol  = nLastCol;
            sal_Int32 nFirstNewRow = bBehind ?  nFirstRow + 1 : nFirstRow - nLines;
            sal_Int32 nLastNewRow  = nFirstNewRow - 1 + nLines;
            if (bAddCols)
            {
                DBG_ASSERT( nFirstCol == nLastCol, "column indices seem broken" );
                nFirstNewCol = bBehind ?  nFirstCol + 1 : nFirstCol - nLines;
                nLastNewCol  = nFirstNewCol - 1 + nLines;
                nFirstNewRow = nFirstRow;
                nLastNewRow  = nLastRow;
            }

			// iterate over all data-sequences for the table
			const Set_DataSequenceRef_t &rSet = aDataSequences[ &rTable ];
			Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
			while (aIt != rSet.end())
			{
//               uno::Reference< chart2::data::XTextualDataSequence > xRef( uno::Reference< chart2::data::XDataSequence >(*aIt), uno::UNO_QUERY );
                uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);  // temporary needed for g++ 3.3.5
                uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
                if (xRef.is())
				{
					const sal_Int32 nLen = xRef->getTextualData().getLength();
					if (nLen > 1) // value data-sequence ?
					{
						SwChartDataSequence *pDataSeq = 0;
						uno::Reference< lang::XUnoTunnel > xTunnel( xRef, uno::UNO_QUERY );
						if(xTunnel.is())
						{
							pDataSeq = reinterpret_cast< SwChartDataSequence * >(
									sal::static_int_cast< sal_IntPtr >( xTunnel->getSomething( SwChartDataSequence::getUnoTunnelId() )));

							if (pDataSeq)
							{
								SwRangeDescriptor aDesc;
								pDataSeq->FillRangeDesc( aDesc );

								chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
								if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
									eDRSource = chart::ChartDataRowSource_ROWS;

								if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
								{
									// add rows: extend affected columns by newly added row cells
                                    pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
								}
								else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
								{
									// add cols: extend affected rows by newly added column cells
                                    pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
								}
							}
						}
					}
				}
				++aIt;
			}

		}
	}
}


// XRangeXMLConversion ---------------------------------------------------

rtl::OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const rtl::OUString& rRangeRepresentation )
    throw ( uno::RuntimeException, lang::IllegalArgumentException )
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    String aRes;
    String aRangeRepresentation( rRangeRepresentation );

    // multiple ranges are delimeted by a ';' like in
    // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
    xub_StrLen nNumRanges = aRangeRepresentation.GetTokenCount( ';' );
    SwTable* pFirstFoundTable = 0;  // to check that only one table will be used
    for (sal_uInt16 i = 0;  i < nNumRanges;  ++i)
    {
        String aRange( aRangeRepresentation.GetToken(i, ';') );
        SwFrmFmt    *pTblFmt  = 0;      // pointer to table format
        // BM: For what should the check be necessary? for #i79009# it is required that NO check is done
//         SwUnoCrsr   *pUnoCrsr = 0;      // here required to check if the cells in the range do actually exist
//         std::auto_ptr< SwUnoCrsr > pAuto( pUnoCrsr );  // to end lifetime of object pointed to by pUnoCrsr
        GetFormatAndCreateCursorFromRangeRep( pDoc, aRange, &pTblFmt, NULL );
        if (!pTblFmt)
            throw lang::IllegalArgumentException();
//    if (!pUnoCrsr)
//        throw uno::RuntimeException();
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if  (pTable->IsTblComplex())
            throw uno::RuntimeException();

        // check that there is only one table used in all ranges
        if (!pFirstFoundTable)
            pFirstFoundTable = pTable;
        if (pTable != pFirstFoundTable)
            throw lang::IllegalArgumentException();

        String aTblName;
        String aStartCell;
        String aEndCell;
        if (!GetTableAndCellsFromRangeRep( aRange, aTblName, aStartCell, aEndCell ))
            throw lang::IllegalArgumentException();

        sal_Int32 nCol, nRow;
        lcl_GetCellPosition( aStartCell, nCol, nRow );
        if (nCol < 0 || nRow < 0)
            throw uno::RuntimeException();

        //!! following objects/functions are implemented in XMLRangeHelper.?xx
        //!! which is a copy of the respective file from chart2 !!
        XMLRangeHelper::CellRange aCellRange;
        aCellRange.aTableName = aTblName;
        aCellRange.aUpperLeft.nColumn   = nCol;
        aCellRange.aUpperLeft.nRow      = nRow;
        aCellRange.aUpperLeft.bIsEmpty  = false;
        if (aStartCell != aEndCell && aEndCell.Len() != 0)
        {
            lcl_GetCellPosition( aEndCell, nCol, nRow );
            if (nCol < 0 || nRow < 0)
                throw uno::RuntimeException();

            aCellRange.aLowerRight.nColumn   = nCol;
            aCellRange.aLowerRight.nRow      = nRow;
            aCellRange.aLowerRight.bIsEmpty  = false;
        }
        String aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
        if (aRes.Len()) // in case of multiple ranges add delimeter
            aRes.AppendAscii( " " );
        aRes += aTmp;
    }

    return aRes;
}

rtl::OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const rtl::OUString& rXMLRange )
    throw ( uno::RuntimeException, lang::IllegalArgumentException )
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    String aRes;
    String aXMLRange( rXMLRange );

    // multiple ranges are delimeted by a ' ' like in
    // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
    xub_StrLen nNumRanges = aXMLRange.GetTokenCount( ' ' );
    rtl::OUString aFirstFoundTable; // to check that only one table will be used
    for (sal_uInt16 i = 0;  i < nNumRanges;  ++i)
    {
        String aRange( aXMLRange.GetToken(i, ' ') );

        //!! following objects and function are implemented in XMLRangeHelper.?xx
        //!! which is a copy of the respective file from chart2 !!
        XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));

        // check that there is only one table used in all ranges
        if (aFirstFoundTable.getLength() == 0)
            aFirstFoundTable = aCellRange.aTableName;
        if (aCellRange.aTableName != aFirstFoundTable)
            throw lang::IllegalArgumentException();

        OUString aTmp( aCellRange.aTableName );
        aTmp += OUString::valueOf((sal_Unicode) '.');
        aTmp += lcl_GetCellName( aCellRange.aUpperLeft.nColumn,
                                 aCellRange.aUpperLeft.nRow );
        // does cell range consist of more than a single cell?
        if (!aCellRange.aLowerRight.bIsEmpty)
        {
            aTmp += OUString::valueOf((sal_Unicode) ':');
            aTmp += lcl_GetCellName( aCellRange.aLowerRight.nColumn,
                                     aCellRange.aLowerRight.nRow );
        }

        if (aRes.Len()) // in case of multiple ranges add delimeter
            aRes.AppendAscii( ";" );
        aRes += String(aTmp);
    }

    return aRes;
}


//////////////////////////////////////////////////////////////////////

SwChartDataSource::SwChartDataSource(
        const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
    aLDS( rLDS )
{
}


SwChartDataSource::~SwChartDataSource()
{
//    delete pTblCrsr;
}


uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return aLDS;
}


OUString SAL_CALL SwChartDataSource::getImplementationName(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return C2U("SwChartDataSource");
}


sal_Bool SAL_CALL SwChartDataSource::supportsService(
        const OUString& rServiceName )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return rServiceName.equalsAscii( SN_DATA_SOURCE );
}


uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    uno::Sequence< OUString > aRes(1);
    aRes.getArray()[0] = C2U( SN_DATA_SOURCE );
    return aRes;
}

//////////////////////////////////////////////////////////////////////

SwChartDataSequence::SwChartDataSequence(
        SwChartDataProvider &rProvider,
        SwFrmFmt   &rTblFmt,
        SwUnoCrsr  *pTableCursor ) :
    SwClient( &rTblFmt ),
    aEvtListeners( GetChartMutex() ),
    aModifyListeners( GetChartMutex() ),
    aRowLabelText( SW_RES( STR_CHART2_ROW_LABEL_TEXT ) ),
    aColLabelText( SW_RES( STR_CHART2_COL_LABEL_TEXT ) ),
    xDataProvider( &rProvider ),
    pDataProvider( &rProvider ),
    pTblCrsr( pTableCursor ),
    aCursorDepend( this, pTableCursor ),
    _pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
{
    bDisposed = sal_False;

    acquire();
    try
    {
        const SwTable* pTable = SwTable::FindTable( &rTblFmt );
        if (pTable)
        {
            uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
            pDataProvider->AddDataSequence( *pTable, xRef );
            pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
        }
        else {
            DBG_ERROR( "table missing" );
        }
    }
    catch (uno::RuntimeException &)
    {
        throw;
    }
    catch (uno::Exception &)
    {
    }
    release();

#if OSL_DEBUG_LEVEL > 1
    OUString aRangeStr( getSourceRangeRepresentation() );

	// check if it can properly convert into a SwUnoTableCrsr
	// which is required for some functions
    SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
    DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
    (void) pUnoTblCrsr;
#endif
}


SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
    SwChartDataSequenceBaseClass(),
    SwClient( rObj.GetFrmFmt() ),
    aEvtListeners( GetChartMutex() ),
    aModifyListeners( GetChartMutex() ),
    aRole( rObj.aRole ),
    aRowLabelText( SW_RES(STR_CHART2_ROW_LABEL_TEXT) ),
    aColLabelText( SW_RES(STR_CHART2_COL_LABEL_TEXT) ),
    xDataProvider( rObj.pDataProvider ),
    pDataProvider( rObj.pDataProvider ),
    pTblCrsr( rObj.pTblCrsr->Clone() ),
    aCursorDepend( this, pTblCrsr ),
    _pPropSet( rObj._pPropSet )
{
    bDisposed = sal_False;

    acquire();
    try
    {
        const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
        if (pTable)
        {
            uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
            pDataProvider->AddDataSequence( *pTable, xRef );
            pDataProvider->addEventListener( dynamic_cast< lang::XEventListener * >(this) );
        }
        else {
            DBG_ERROR( "table missing" );
        }
    }
    catch (uno::RuntimeException &)
    {
        throw;
    }
    catch (uno::Exception &)
    {
    }
    release();

#if OSL_DEBUG_LEVEL > 1
    OUString aRangeStr( getSourceRangeRepresentation() );

    // check if it can properly convert into a SwUnoTableCrsr
	// which is required for some functions
    SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
    DBG_ASSERT(pUnoTblCrsr, "SwChartDataSequence: cursor not SwUnoTableCrsr");
    (void) pUnoTblCrsr;
#endif
}


SwChartDataSequence::~SwChartDataSequence()
{
    // since the data-provider holds only weak references to the data-sequence
    // there should be no need here to release them explicitly...

    delete pTblCrsr;
}


const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
{
    static uno::Sequence< sal_Int8 > aSeq = ::CreateUnoTunnelId();
    return aSeq;
}


sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
    throw(uno::RuntimeException)
{
    if( rId.getLength() == 16
        && 0 == rtl_compareMemory( getUnoTunnelId().getConstArray(),
                                        rId.getConstArray(), 16 ) )
    {
        return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(this) );
    }
    return 0;
}


uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Sequence< uno::Any > aRes;
    SwFrmFmt* pTblFmt = GetFrmFmt();
    if(pTblFmt)
    {
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if(!pTable->IsTblComplex())
        {
            SwRangeDescriptor aDesc;
            if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
            {
				//!! make copy of pTblCrsr (SwUnoCrsr )
				// keep original cursor and make copy of it that gets handed
				// over to the SwXCellRange object which takes ownership and
				// thus will destroy the copy later.
                SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
                aRange.GetDataSequence( &aRes, 0, 0 );
            }
        }
    }
    return aRes;
}


OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    String aRes;
    SwFrmFmt* pTblFmt = GetFrmFmt();
    if (pTblFmt)
    {
        aRes = pTblFmt->GetName();
        String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
        DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
        aRes += (sal_Unicode) '.';
        aRes += aCellRange;
    }
    return aRes;
}

uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
        chart2::data::LabelOrigin eLabelOrigin )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Sequence< OUString > aLabels;

    {
        SwRangeDescriptor aDesc;
        sal_Bool bOk sal_False;
        SwFrmFmt* pTblFmt = GetFrmFmt();
        SwTable* pTable = pTblFmt ? SwTable::FindTable( pTblFmt ) : 0;
        if (!pTblFmt || !pTable || pTable->IsTblComplex())
            throw uno::RuntimeException();
        else
        {
            String aCellRange( GetCellRangeName( *pTblFmt, *pTblCrsr ) );
            DBG_ASSERT( aCellRange.Len() != 0, "failed to get cell range" );
            bOk = FillRangeDescriptor( aDesc, aCellRange );
            DBG_ASSERT( bOk, "falied to get SwRangeDescriptor" );
        }
        if (bOk)
        {
            aDesc.Normalize();
            sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
            sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
            DBG_ASSERT( nColSpan == 1 || nRowSpan == 1,
                    "unexpected range of selected cells" );

            String aTxt;    // label text to be returned
            sal_Bool bReturnEmptyTxt = sal_False;
            sal_Bool bUseCol = sal_True;
            if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
                bUseCol = sal_True;
            else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
                bUseCol = sal_False;
            else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
            {
                bUseCol = nColSpan < nRowSpan;
                bReturnEmptyTxt = nColSpan == nRowSpan;
            }
            else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
            {
                bUseCol = nColSpan > nRowSpan;
                bReturnEmptyTxt = nColSpan == nRowSpan;
            }
            else {
                DBG_ERROR( "unexpected case" );
            }

            // build label sequence
            //
            sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
            aLabels.realloc( nSeqLen );
            OUString *pLabels = aLabels.getArray();
            for (sal_Int32 i = 0;  i < nSeqLen;  ++i)
            {
                if (!bReturnEmptyTxt)
                {
                    aTxt = bUseCol ? aColLabelText : aRowLabelText;
                    sal_Int32 nCol = aDesc.nLeft;
                    sal_Int32 nRow = aDesc.nTop;
                    if (bUseCol)
                        nCol = nCol + i;
                    else
                        nRow = nRow + i;
                    String aCellName( lcl_GetCellName( nCol, nRow ) );

                    xub_StrLen nLen = aCellName.Len();
                    if (nLen)
                    {
                        const sal_Unicode *pBuf = aCellName.GetBuffer();
                        const sal_Unicode *pEnd = pBuf + nLen;
                        while (pBuf < pEnd && !('0' <= *pBuf && *pBuf <= '9'))
                            ++pBuf;
                        // start of number found?
                        if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
                        {
                            String aRplc;
                            String aNew;
                            if (bUseCol)
                            {
								aRplc = String::CreateFromAscii( "%COLUMNLETTER" );
                                aNew = String( aCellName.GetBuffer(), static_cast<xub_StrLen>(pBuf - aCellName.GetBuffer()) );
                            }
                            else
                            {
                                aRplc = String::CreateFromAscii( "%ROWNUMBER" );
                                aNew = String( pBuf, static_cast<xub_StrLen>((aCellName.GetBuffer() + nLen) - pBuf) );
                            }
                            xub_StrLen nPos = aTxt.Search( aRplc );
                            if (nPos != STRING_NOTFOUND)
                                aTxt = aTxt.Replace( nPos, aRplc.Len(), aNew );
                        }
                    }
                }
                pLabels[i] = aTxt;
            }
        }
    }

    return aLabels;
}

::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
    ::sal_Int32 /*nIndex*/ )
    throw (lang::IndexOutOfBoundsException,
           uno::RuntimeException)
{
    return 0;
}



uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Sequence< OUString > aRes;
    SwFrmFmt* pTblFmt = GetFrmFmt();
    if(pTblFmt)
    {
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if(!pTable->IsTblComplex())
        {
            SwRangeDescriptor aDesc;
            if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
            {
				//!! make copy of pTblCrsr (SwUnoCrsr )
				// keep original cursor and make copy of it that gets handed
				// over to the SwXCellRange object which takes ownership and
				// thus will destroy the copy later.
                SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );
                aRange.GetDataSequence( 0, &aRes, 0 );
            }
        }
    }
    return aRes;
}


uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Sequence< double > aRes;
    SwFrmFmt* pTblFmt = GetFrmFmt();
    if(pTblFmt)
    {
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if(!pTable->IsTblComplex())
        {
            SwRangeDescriptor aDesc;
            if (FillRangeDescriptor( aDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) ))
            {
                //!! make copy of pTblCrsr (SwUnoCrsr )
                // keep original cursor and make copy of it that gets handed
                // over to the SwXCellRange object which takes ownership and
                // thus will destroy the copy later.
                SwXCellRange aRange( pTblCrsr->Clone(), *pTblFmt, aDesc );

                // get numerical values and make an effort to return the
                // numerical value for text formatted cells
                aRange.GetDataSequence( 0, 0, &aRes, sal_True );
            }
        }
    }
    return aRes;
}


uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();
    return new SwChartDataSequence( *this );
}


uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    static uno::Reference< beans::XPropertySetInfo > xRes = _pPropSet->getPropertySetInfo();
    return xRes;
}


void SAL_CALL SwChartDataSequence::setPropertyValue(
        const OUString& rPropertyName,
        const uno::Any& rValue )
    throw (beans::UnknownPropertyException, beans::PropertyVetoException, lang::IllegalArgumentException, lang::WrappedTargetException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
    {
        if ( !(rValue >>= aRole) )
            throw lang::IllegalArgumentException();
    }
    else
        throw beans::UnknownPropertyException();
}


uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
        const OUString& rPropertyName )
    throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Any aRes;
    if (rPropertyName.equalsAscii( SW_PROP_NAME_STR( UNO_NAME_ROLE )))
        aRes <<= aRole;
    else
        throw beans::UnknownPropertyException();

    return aRes;
}


void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
    throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
{
    //vos::OGuard aGuard( Application::GetSolarMutex() );
    DBG_ERROR( "not implemented" );
}


void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
    throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
{
    //vos::OGuard aGuard( Application::GetSolarMutex() );
    DBG_ERROR( "not implemented" );
}


void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
    throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
{
    //vos::OGuard aGuard( Application::GetSolarMutex() );
    DBG_ERROR( "not implemented" );
}


void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
        const OUString& /*rPropertyName*/,
        const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
    throw (beans::UnknownPropertyException, lang::WrappedTargetException, uno::RuntimeException)
{
    //vos::OGuard aGuard( Application::GetSolarMutex() );
    DBG_ERROR( "not implemented" );
}


OUString SAL_CALL SwChartDataSequence::getImplementationName(  )
    throw (uno::RuntimeException)
{
    return C2U("SwChartDataSequence");
}


sal_Bool SAL_CALL SwChartDataSequence::supportsService(
        const OUString& rServiceName )
    throw (uno::RuntimeException)
{
    return rServiceName.equalsAscii( SN_DATA_SEQUENCE );
}


uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    uno::Sequence< OUString > aRes(1);
    aRes.getArray()[0] = C2U( SN_DATA_SEQUENCE );
    return aRes;
}


void SwChartDataSequence::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew)
{
    ClientModify(this, pOld, pNew );

    // table was deleted or cursor was deleted
    if(!GetRegisteredIn() || !aCursorDepend.GetRegisteredIn())
	{
        pTblCrsr = 0;
        dispose();
	}
	else
	{
        setModified( sal_True );
	}
}


sal_Bool SAL_CALL SwChartDataSequence::isModified(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    return sal_True;
}


void SAL_CALL SwChartDataSequence::setModified(
        ::sal_Bool bModified )
    throw (beans::PropertyVetoException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    if (bModified)
		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
}


void SAL_CALL SwChartDataSequence::addModifyListener(
        const uno::Reference< util::XModifyListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aModifyListeners.addInterface( rxListener );
}


void SAL_CALL SwChartDataSequence::removeModifyListener(
        const uno::Reference< util::XModifyListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aModifyListeners.removeInterface( rxListener );
}


void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
    throw (uno::RuntimeException)
{
    if (bDisposed)
        throw lang::DisposedException();
    if (rSource.Source == xDataProvider)
    {
        pDataProvider = 0;
        xDataProvider.clear();
    }
}


void SAL_CALL SwChartDataSequence::dispose(  )
    throw (uno::RuntimeException)
{
    sal_Bool bMustDispose( sal_False );
	{
		osl::MutexGuard  aGuard( GetChartMutex() );
        bMustDispose = !bDisposed;
		if (!bDisposed)
			bDisposed = sal_True;
	}
    if (bMustDispose)
    {
        bDisposed = sal_True;
        if (pDataProvider)
        {
            const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
            if (pTable)
            {
                uno::Reference< chart2::data::XDataSequence > xRef( dynamic_cast< chart2::data::XDataSequence * >(this), uno::UNO_QUERY );
                pDataProvider->RemoveDataSequence( *pTable, xRef );
            }
            else {
                DBG_ERROR( "table missing" );
            }
        }

        // require listeners to release references to this object
        lang::EventObject aEvtObj( dynamic_cast< chart2::data::XDataSequence * >(this) );
        aModifyListeners.disposeAndClear( aEvtObj );
        aEvtListeners.disposeAndClear( aEvtObj );
    }
}


void SAL_CALL SwChartDataSequence::addEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.addInterface( rxListener );
}


void SAL_CALL SwChartDataSequence::removeEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.removeInterface( rxListener );
}


sal_Bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
{
#if OSL_DEBUG_LEVEL > 1
	String aBoxName( rBox.GetName() );
#endif

    // to be set if the last box of the data-sequence was removed here
    sal_Bool bNowEmpty = sal_False;

    // if the implementation cursor gets affected (i.e. thew box where it is located
    // in gets removed) we need to move it before that... (otherwise it does not need to change)
    //
    const SwStartNode* pPointStartNode = pTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
    const SwStartNode* pMarkStartNode  = pTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
    //
    if (!pTblCrsr->HasMark() || (pPointStartNode == rBox.GetSttNd()  &&  pMarkStartNode == rBox.GetSttNd()))
    {
        bNowEmpty = sal_True;
    }
    else if (pPointStartNode == rBox.GetSttNd()  ||  pMarkStartNode == rBox.GetSttNd())
    {
        sal_Int32 nPointRow = -1, nPointCol = -1;
        sal_Int32 nMarkRow  = -1, nMarkCol  = -1;
        const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
        String aPointCellName( pTable->GetTblBox( pPointStartNode->GetIndex() )->GetName() );
		String aMarkCellName( pTable->GetTblBox( pMarkStartNode->GetIndex() )->GetName() );

        lcl_GetCellPosition( aPointCellName, nPointCol, nPointRow );
        lcl_GetCellPosition( aMarkCellName,  nMarkCol,  nMarkRow );
        DBG_ASSERT( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
        DBG_ASSERT( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );

        // move vertical or horizontal?
        DBG_ASSERT( nPointRow == nMarkRow || nPointCol == nMarkCol,
                "row/col indices not matching" );
        DBG_ASSERT( nPointRow != nMarkRow || nPointCol != nMarkCol,
                "point and mark are identical" );
        sal_Bool bMoveVertical      = (nPointCol == nMarkCol);
        sal_Bool bMoveHorizontal    = (nPointRow == nMarkRow);

        // get movement direction
        sal_Bool bMoveLeft  = sal_False;    // move left or right?
        sal_Bool bMoveUp    = sal_False;    // move up or down?
        if (bMoveVertical)
        {
            if (pPointStartNode == rBox.GetSttNd()) // move point?
                bMoveUp = nPointRow > nMarkRow;
            else    // move mark
                bMoveUp = nMarkRow > nPointRow;
        }
        else if (bMoveHorizontal)
        {
            if (pPointStartNode == rBox.GetSttNd()) // move point?
                bMoveLeft = nPointCol > nMarkCol;
            else    // move mark
                bMoveLeft = nMarkCol > nPointCol;
        }
        else {
            DBG_ERROR( "neither vertical nor horizontal movement" );
        }

        // get new box (position) to use...
        sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
        sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
		if (bMoveVertical)
			nRow += bMoveUp ? -1 : +1;
		if (bMoveHorizontal)
			nCol += bMoveLeft ? -1 : +1;
        String aNewCellName = lcl_GetCellName( nCol, nRow );
        SwTableBox* pNewBox = (SwTableBox*) pTable->GetTblBox( aNewCellName );

        if (pNewBox)    // set new position (cell range) to use
        {
            // So erhält man den ersten Inhaltsnode in einer gegebenen Zelle:
            // Zunächst einen SwNodeIndex auf den Node hinter dem SwStartNode der Box...
            SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
            // Dies kann ein SwCntntNode sein, kann aber auch ein Tabellen oder Sectionnode sein,
            // deshalb das GoNext;
            SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode();
            if (!pCNd)
                pCNd = GetFrmFmt()->GetDoc()->GetNodes().GoNext( &aIdx );
            //und damit kann man z.B. eine SwPosition erzeugen:
            SwPosition aNewPos( *pCNd );   // new position to beused with cursor

            // if the mark is to be changed make sure there is one...
            if (pMarkStartNode == rBox.GetSttNd() && !pTblCrsr->HasMark())
                pTblCrsr->SetMark();

            // set cursor to new position...
            SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
                        pTblCrsr->GetPoint() : pTblCrsr->GetMark();
            if (pPos)
            {
                pPos->nNode     = aNewPos.nNode;
                pPos->nContent  = aNewPos.nContent;
            }
            else {
                DBG_ERROR( "neither point nor mark available for change" );
            }
        }
        else {
            DBG_ERROR( "failed to get position" );
        }
    }

    return bNowEmpty;
}


void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
{
    SwFrmFmt* pTblFmt = GetFrmFmt();
    if(pTblFmt)
    {
        SwTable* pTable = SwTable::FindTable( pTblFmt );
        if(!pTable->IsTblComplex())
        {
            FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTblFmt, *pTblCrsr ) );
        }
    }
}

/**
SwChartDataSequence::ExtendTo

extends the data-sequence by new cells added at the end of the direction
the data-sequence points to.
If the cells are already within the range of the sequence nothing needs
to be done.
If the cells are beyond the end of the sequence (are not adjacent to the
current last cell) nothing can be done. Only if the cells are adjacent to
the last cell they can be added.

@returns     true if the data-sequence was changed.
@param       bExtendCols
             specifies if columns or rows are to be extended
@param       nFirstNew
             index of first new row/col to be included in data-sequence
@param       nLastNew
             index of last new row/col to be included in data-sequence
*/
bool SwChartDataSequence::ExtendTo( bool bExtendCol,
        sal_Int32 nFirstNew, sal_Int32 nCount )
{
    bool bChanged = false;

    SwUnoTableCrsr* pUnoTblCrsr = dynamic_cast<SwUnoTableCrsr*>(pTblCrsr);
    //pUnoTblCrsr->MakeBoxSels();

    const SwStartNode *pStartNd  = 0;
    const SwTableBox  *pStartBox = 0;
    const SwTableBox  *pEndBox   = 0;

    const SwTable* pTable = SwTable::FindTable( GetFrmFmt() );
	DBG_ASSERT( !pTable->IsTblComplex(), "table too complex" );
	if (nCount < 1 || nFirstNew < 0 || pTable->IsTblComplex())
		return false;

	//
	// get range descriptor (cell range) for current data-sequence
	//
    pStartNd = pUnoTblCrsr->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
    pEndBox = pTable->GetTblBox( pStartNd->GetIndex() );
    const String aEndBox( pEndBox->GetName() );
	//
    pStartNd = pUnoTblCrsr->GetMark()->nNode.GetNode().FindTableBoxStartNode();
    pStartBox = pTable->GetTblBox( pStartNd->GetIndex() );
    const String aStartBox( pStartBox->GetName() );
	//
    String aCellRange( aStartBox );     // note that cell range here takes the newly added rows/cols already into account
    aCellRange.AppendAscii( ":" );
    aCellRange += aEndBox;
    SwRangeDescriptor aDesc;
    FillRangeDescriptor( aDesc, aCellRange );

    String aNewStartCell;
    String aNewEndCell;
    if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
    {
        // new column cells adjacent to the bottom of the
		// current data-sequence to be added...
        DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
        aNewStartCell = lcl_GetCellName(aDesc.nLeft,  aDesc.nTop);
        aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
		bChanged = true;
    }
    else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
    {
        // new column cells adjacent to the top of the
		// current data-sequence to be added...
        DBG_ASSERT( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
        aNewStartCell = lcl_GetCellName(aDesc.nLeft,  aDesc.nTop - nCount);
        aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
		bChanged = true;
    }
    else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
    {
        // new row cells adjacent to the right of the
		// current data-sequence to be added...
        DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
        aNewStartCell = lcl_GetCellName(aDesc.nLeft, aDesc.nTop);
        aNewEndCell   = lcl_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
		bChanged = true;
    }
    else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
    {
        // new row cells adjacent to the left of the
		// current data-sequence to be added...
        DBG_ASSERT( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
        aNewStartCell = lcl_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
        aNewEndCell   = lcl_GetCellName(aDesc.nRight, aDesc.nBottom);
		bChanged = true;
    }

	if (bChanged)
	{
		// move table cursor to new start and end of data-sequence
        const SwTableBox *pNewStartBox = pTable->GetTblBox( aNewStartCell );
        const SwTableBox *pNewEndBox   = pTable->GetTblBox( aNewEndCell );
        pUnoTblCrsr->SetMark();
        pUnoTblCrsr->GetPoint()->nNode = *pNewEndBox->GetSttNd();
        pUnoTblCrsr->GetMark()->nNode  = *pNewStartBox->GetSttNd();
        pUnoTblCrsr->Move( fnMoveForward, fnGoNode );
        pUnoTblCrsr->MakeBoxSels();
	}

    return bChanged;
}

//////////////////////////////////////////////////////////////////////

SwChartLabeledDataSequence::SwChartLabeledDataSequence() :
    aEvtListeners( GetChartMutex() ),
    aModifyListeners( GetChartMutex() )
{
    bDisposed = sal_False;
}


SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
{
}


uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();
    return xData;
}


void SwChartLabeledDataSequence::SetDataSequence(
		uno::Reference< chart2::data::XDataSequence >& rxDest,
		const uno::Reference< chart2::data::XDataSequence >& rxSource)
{
    uno::Reference< util::XModifyListener >  xML( dynamic_cast< util::XModifyListener* >(this), uno::UNO_QUERY );
    uno::Reference< lang::XEventListener >   xEL( dynamic_cast< lang::XEventListener* >(this), uno::UNO_QUERY );

    // stop listening to old data-sequence
    uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
    if (xMB.is())
        xMB->removeModifyListener( xML );
    uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
    if (xC.is())
        xC->removeEventListener( xEL );

    rxDest = rxSource;

    // start listening to new data-sequence
    xC = uno::Reference< lang::XComponent >( rxDest, uno::UNO_QUERY );
    if (xC.is())
        xC->addEventListener( xEL );
    xMB = uno::Reference< util::XModifyBroadcaster >( rxDest, uno::UNO_QUERY );
    if (xMB.is())
        xMB->addModifyListener( xML );
}


void SAL_CALL SwChartLabeledDataSequence::setValues(
        const uno::Reference< chart2::data::XDataSequence >& rxSequence )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    if (xData != rxSequence)
    {
		SetDataSequence( xData, rxSequence );
        // inform listeners of changes
		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
    }
}


uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();
    return xLabels;
}


void SAL_CALL SwChartLabeledDataSequence::setLabel(
        const uno::Reference< chart2::data::XDataSequence >& rxSequence )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    if (xLabels != rxSequence)
    {
		SetDataSequence( xLabels, rxSequence );
        // inform listeners of changes
		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
    }
}


uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    if (bDisposed)
        throw lang::DisposedException();

    uno::Reference< util::XCloneable > xRes;

    uno::Reference< util::XCloneable > xDataCloneable( xData, uno::UNO_QUERY );
    uno::Reference< util::XCloneable > xLabelsCloneable( xLabels, uno::UNO_QUERY );
    SwChartLabeledDataSequence *pRes = new SwChartLabeledDataSequence();
    if (xDataCloneable.is())
    {
        uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
        pRes->setValues( xDataClone );
    }

    if (xLabelsCloneable.is())
    {
        uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
        pRes->setLabel( xLabelsClone );
    }
    xRes = pRes;
    return xRes;
}


OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName(  )
    throw (uno::RuntimeException)
{
    return C2U("SwChartLabeledDataSequence");
}


sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
        const OUString& rServiceName )
    throw (uno::RuntimeException)
{
    return rServiceName.equalsAscii( SN_LABELED_DATA_SEQUENCE );
}


uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames(  )
    throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    uno::Sequence< OUString > aRes(1);
    aRes.getArray()[0] = C2U( SN_LABELED_DATA_SEQUENCE );
    return aRes;
}


void SAL_CALL SwChartLabeledDataSequence::disposing(
        const lang::EventObject& rSource )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    uno::Reference< uno::XInterface > xRef( rSource.Source );
    if (xRef == xData)
        xData.clear();
    if (xRef == xLabels)
        xLabels.clear();
    if (!xData.is() && !xLabels.is())
        dispose();
}


void SAL_CALL SwChartLabeledDataSequence::modified(
        const lang::EventObject& rEvent )
    throw (uno::RuntimeException)
{
    if (rEvent.Source == xData || rEvent.Source == xLabels)
    {
		LaunchModifiedEvent( aModifyListeners, dynamic_cast< XModifyBroadcaster * >(this) );
    }
}


void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
        const uno::Reference< util::XModifyListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aModifyListeners.addInterface( rxListener );
}


void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
        const uno::Reference< util::XModifyListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aModifyListeners.removeInterface( rxListener );
}


void SAL_CALL SwChartLabeledDataSequence::dispose(  )
    throw (uno::RuntimeException)
{
    sal_Bool bMustDispose( sal_False );
	{
		osl::MutexGuard  aGuard( GetChartMutex() );
        bMustDispose = !bDisposed;
		if (!bDisposed)
			bDisposed = sal_True;
	}
    if (bMustDispose)
    {
        bDisposed = sal_True;

        // require listeners to release references to this object
        lang::EventObject aEvtObj( dynamic_cast< chart2::data::XLabeledDataSequence * >(this) );
        aModifyListeners.disposeAndClear( aEvtObj );
        aEvtListeners.disposeAndClear( aEvtObj );
    }
}


void SAL_CALL SwChartLabeledDataSequence::addEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.addInterface( rxListener );
}


void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
        const uno::Reference< lang::XEventListener >& rxListener )
    throw (uno::RuntimeException)
{
    osl::MutexGuard  aGuard( GetChartMutex() );
    if (!bDisposed && rxListener.is())
        aEvtListeners.removeInterface( rxListener );
}

//////////////////////////////////////////////////////////////////////
