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

#ifdef _MSC_VER
#pragma hdrstop
#endif

#include <bf_sfx2/app.hxx>

#include "funcuno.hxx"
#include "miscuno.hxx"
#include "cellsuno.hxx"
#include <vcl/svapp.hxx>
#include "scdll.hxx"
#include "document.hxx"
#include "callform.hxx"
#include "addincol.hxx"
#include "rangeseq.hxx"
#include "cell.hxx"
#include "optuno.hxx"

// for lcl_CopyData:
#include "markdata.hxx"
#include "patattr.hxx"
#include "docpool.hxx"
#include "attrib.hxx"
namespace binfilter {

using namespace ::com::sun::star;

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

//  registered as implementation for service FunctionAccess,
//  also supports service SpreadsheetDocumentSettings (to set null date etc.)

#define SCFUNCTIONACCESS_SERVICE    "com.sun.star.sheet.FunctionAccess"
#define SCDOCSETTINGS_SERVICE       "com.sun.star.sheet.SpreadsheetDocumentSettings"

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

// helper to use cached document if not in use, temporary document otherwise

class ScTempDocSource
{
private:
    ScTempDocCache& rCache;
    ScDocument*     pTempDoc;

    static ScDocument*  CreateDocument();       // create and initialize doc

public:
                ScTempDocSource( ScTempDocCache& rDocCache );
                ~ScTempDocSource();

    ScDocument*     GetDocument();
};

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

// static
ScDocument* ScTempDocSource::CreateDocument()
{
    ScDocument* pDoc = new ScDocument;                  // SCDOCMODE_DOCUMENT
    pDoc->MakeTable( 0 );
    return pDoc;
}

ScTempDocSource::ScTempDocSource( ScTempDocCache& rDocCache ) :
    rCache( rDocCache ),
    pTempDoc( NULL )
{
    if ( rCache.IsInUse() )
        pTempDoc = CreateDocument();
    else
    {
        rCache.SetInUse( TRUE );
        if ( !rCache.GetDocument() )
            rCache.SetDocument( CreateDocument() );
    }
}

ScTempDocSource::~ScTempDocSource()
{
    if ( pTempDoc )
        delete pTempDoc;
    else
        rCache.SetInUse( FALSE );
}

ScDocument* ScTempDocSource::GetDocument()
{
    if ( pTempDoc )
        return pTempDoc;
    else
        return rCache.GetDocument();
}


ScTempDocCache::ScTempDocCache() :
    pDoc( NULL ),
    bInUse( FALSE )
{
}

ScTempDocCache::~ScTempDocCache()
{
    DBG_ASSERT( !bInUse, "ScTempDocCache dtor: bInUse" );
    delete pDoc;
}

void ScTempDocCache::SetDocument( ScDocument* pNew )
{
    DBG_ASSERT( !pDoc, "ScTempDocCache::SetDocument: already set" );
    pDoc = pNew;
}

void ScTempDocCache::Clear()
{
    DBG_ASSERT( !bInUse, "ScTempDocCache::Clear: bInUse" );
    delete pDoc;
    pDoc = NULL;
}

//  copy results from one document into another
//! merge this with ScAreaLink::Refresh
//! copy directly without a clipboard document?

BOOL lcl_CopyData( ScDocument* pSrcDoc, const ScRange& rSrcRange,
                    ScDocument* pDestDoc, const ScAddress& rDestPos )
{
    USHORT nSrcTab = rSrcRange.aStart.Tab();
    USHORT nDestTab = rDestPos.Tab();

    ScRange aNewRange( rDestPos, ScAddress(
                rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + rDestPos.Col(),
                rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + rDestPos.Row(),
                nDestTab ) );

    ScDocument* pClipDoc = new ScDocument( SCDOCMODE_CLIP );
    ScMarkData aSourceMark;
    aSourceMark.SelectOneTable( nSrcTab );      // for CopyToClip
    aSourceMark.SetMarkArea( rSrcRange );
    pSrcDoc->CopyToClip( rSrcRange.aStart.Col(),rSrcRange.aStart.Row(),
                         rSrcRange.aEnd.Col(),rSrcRange.aEnd.Row(),
                         FALSE, pClipDoc, FALSE, &aSourceMark );

    if ( pClipDoc->HasAttrib( 0,0,nSrcTab, MAXCOL,MAXROW,nSrcTab,
                                HASATTR_MERGED | HASATTR_OVERLAPPED ) )
    {
        ScPatternAttr aPattern( pSrcDoc->GetPool() );
        aPattern.GetItemSet().Put( ScMergeAttr() );             // Defaults
        aPattern.GetItemSet().Put( ScMergeFlagAttr() );
        pClipDoc->ApplyPatternAreaTab( 0,0, MAXCOL,MAXROW, nSrcTab, aPattern );
    }

    ScMarkData aDestMark;
    aDestMark.SelectOneTable( nDestTab );
    aDestMark.SetMarkArea( aNewRange );
    pDestDoc->CopyFromClip( aNewRange, aDestMark, IDF_ALL & ~IDF_FORMULA, NULL, pClipDoc, FALSE );

    delete pClipDoc;
    return TRUE;
}

ScFunctionAccess::ScFunctionAccess()
    : pOptions( NULL )
    , bInvalid( FALSE )
{
    StartListening( *SFX_APP() );       // for SFX_HINT_DEINITIALIZING
}

ScFunctionAccess::~ScFunctionAccess()
{
    delete pOptions;
}

void ScFunctionAccess::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
    if ( rHint.ISA(SfxSimpleHint) &&
        ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_DEINITIALIZING )
    {
        //  document must not be used anymore
        aDocCache.Clear();
        bInvalid = TRUE;
    }
}

// stuff for exService_...

uno::Reference<uno::XInterface> SAL_CALL ScFunctionAccess_CreateInstance(
                        const uno::Reference<lang::XMultiServiceFactory>& )
{
    SolarMutexGuard aGuard;
    SC_DLL()->Load();       // load module
    static uno::Reference< uno::XInterface > xInst = (::cppu::OWeakObject*) new ScFunctionAccess;
    return xInst;
}

::rtl::OUString ScFunctionAccess::getImplementationName_Static()
{
    return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "stardiv.StarCalc.ScFunctionAccess" ));
}

uno::Sequence< ::rtl::OUString> ScFunctionAccess::getSupportedServiceNames_Static()
{
    uno::Sequence< ::rtl::OUString> aRet(1);
    ::rtl::OUString* pArray = aRet.getArray();
    pArray[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( SCFUNCTIONACCESS_SERVICE ));
    return aRet;
}

// XServiceInfo

::rtl::OUString SAL_CALL ScFunctionAccess::getImplementationName() throw(uno::RuntimeException)
{
    return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ScFunctionAccess" ));
}

sal_Bool SAL_CALL ScFunctionAccess::supportsService( const ::rtl::OUString& rServiceName )
                                                    throw(uno::RuntimeException)
{
    String aServiceStr = rServiceName;
    return aServiceStr.EqualsAscii( SCFUNCTIONACCESS_SERVICE ) ||
           aServiceStr.EqualsAscii( SCDOCSETTINGS_SERVICE );
}

uno::Sequence< ::rtl::OUString> SAL_CALL ScFunctionAccess::getSupportedServiceNames()
                                                    throw(uno::RuntimeException)
{
    uno::Sequence< ::rtl::OUString> aRet(2);
    ::rtl::OUString* pArray = aRet.getArray();
    pArray[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( SCFUNCTIONACCESS_SERVICE ));
    pArray[1] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( SCDOCSETTINGS_SERVICE ));
    return aRet;
}

// XPropertySet (document settings)

uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFunctionAccess::getPropertySetInfo()
                                                        throw(uno::RuntimeException)
{
    SolarMutexGuard aGuard;
    static uno::Reference<beans::XPropertySetInfo> aRef =
        new SfxItemPropertySetInfo( ScDocOptionsHelper::GetPropertyMap() );
    return aRef;
}

void SAL_CALL ScFunctionAccess::setPropertyValue(
                        const ::rtl::OUString& aPropertyName, const uno::Any& aValue )
                throw(beans::UnknownPropertyException, beans::PropertyVetoException,
                        lang::IllegalArgumentException, lang::WrappedTargetException,
                        uno::RuntimeException)
{
    SolarMutexGuard aGuard;

    if ( !pOptions )
        pOptions = new ScDocOptions();

    // options aren't initialized from configuration - always get the same default behaviour

    BOOL bDone = ScDocOptionsHelper::setPropertyValue( *pOptions, aPropertyName, aValue );
    if (!bDone)
        throw beans::UnknownPropertyException();
}

uno::Any SAL_CALL ScFunctionAccess::getPropertyValue( const ::rtl::OUString& aPropertyName )
                throw(beans::UnknownPropertyException, lang::WrappedTargetException,
                        uno::RuntimeException)
{
    SolarMutexGuard aGuard;

    if ( !pOptions )
        pOptions = new ScDocOptions();

    // options aren't initialized from configuration - always get the same default behaviour

    return ScDocOptionsHelper::getPropertyValue( *pOptions, aPropertyName );
}

SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess )

// XFunctionAccess

BOOL lcl_AddFunctionToken( ScTokenArray& rArray, const ::rtl::OUString& rName )
{
    // function names are always case-insensitive
    String aUpper( ScGlobal::pCharClass->uppercase( rName ) );

    // same options as in ScCompiler::IsOpCode:
    // 1. built-in function name

    DBG_ASSERT( ScCompiler::pSymbolHashMapEnglish, "no symbol hash map" );
    if (!ScCompiler::pSymbolHashMapEnglish)
        return FALSE;

    ScOpCodeHashMap::const_iterator iLook( ScCompiler::pSymbolHashMapEnglish->find( aUpper ) );
    if ( iLook != ScCompiler::pSymbolHashMapEnglish->end() )
    {
        rArray.AddOpCode( iLook->second );
        return TRUE;
    }

    // 2. old add in functions

    USHORT nIndex;
    if ( ScGlobal::GetFuncCollection()->SearchFunc( aUpper, nIndex ) )
    {
        rArray.AddExternal( aUpper.GetBuffer() );
        return TRUE;
    }

    // 3. new (uno) add in functions

    String aIntName = ScGlobal::GetAddInCollection()->FindFunction( aUpper, FALSE );
    if (aIntName.Len())
    {
        rArray.AddExternal( aIntName.GetBuffer() );     // international name
        return TRUE;
    }

    return FALSE;       // no valid function name
}

void lcl_AddRef( ScTokenArray& rArray, long nStartRow, long nColCount, long nRowCount )
{
    ComplRefData aRef;
    aRef.InitFlags();
    aRef.Ref1.nTab = 0;
    aRef.Ref2.nTab = 0;
    aRef.Ref1.nCol = 0;
    aRef.Ref1.nRow = (USHORT) nStartRow;
    aRef.Ref2.nCol = (USHORT) (nColCount - 1);
    aRef.Ref2.nRow = (USHORT) (nStartRow + nRowCount - 1);
    rArray.AddDoubleReference(aRef);
}

uno::Any SAL_CALL ScFunctionAccess::callFunction( const ::rtl::OUString& aName,
                            const uno::Sequence<uno::Any>& aArguments )
                throw(container::NoSuchElementException, lang::IllegalArgumentException,
                        uno::RuntimeException)
{
    SolarMutexGuard aGuard;

    if (bInvalid)
        throw uno::RuntimeException();

    // use cached document if not in use, temporary document otherwise
    //  (deleted in ScTempDocSource dtor)
    ScTempDocSource aSource( aDocCache );
    ScDocument* pDoc = aSource.GetDocument();

    if (!ScCompiler::pSymbolTableEnglish)
    {
        ScCompiler aComp( pDoc, ScAddress() );
        aComp.SetCompileEnglish( TRUE );        // setup english symbol table
    }

    //
    //  find function
    //

    ScTokenArray aTokenArr;
    if ( !lcl_AddFunctionToken( aTokenArr, aName ) )
    {
        // function not found
        throw container::NoSuchElementException();
    }

    //
    //  set options (null date, etc.)
    //

    if ( pOptions )
        pDoc->SetDocOptions( *pOptions );

    //
    //  add arguments to token array
    //

    BOOL bArgErr = FALSE;
    BOOL bOverflow = FALSE;
    long nDocRow = 0;
    long nArgCount = aArguments.getLength();
    const uno::Any* pArgArr = aArguments.getConstArray();

    aTokenArr.AddOpCode(ocOpen);
    for (long nPos=0; nPos<nArgCount; nPos++)
    {
        if ( nPos > 0 )
            aTokenArr.AddOpCode(ocSep);

        const uno::Any& rArg = pArgArr[nPos];

        uno::TypeClass eClass = rArg.getValueTypeClass();
        uno::Type aType = rArg.getValueType();
        if ( eClass == uno::TypeClass_BYTE ||
                eClass == uno::TypeClass_SHORT ||
                eClass == uno::TypeClass_UNSIGNED_SHORT ||
                eClass == uno::TypeClass_LONG ||
                eClass == uno::TypeClass_UNSIGNED_LONG ||
                eClass == uno::TypeClass_FLOAT ||
                eClass == uno::TypeClass_DOUBLE )
        {
            //  #87871# accept integer types because Basic passes a floating point
            //  variable as byte, short or long if it's an integer number.
            double fVal(0.0);
            rArg >>= fVal;
            aTokenArr.AddDouble( fVal );
        }
        else if ( eClass == uno::TypeClass_STRING )
        {
            ::rtl::OUString aUStr;
            rArg >>= aUStr;
            String aStr( aUStr );
            aTokenArr.AddString( aStr.GetBuffer() );
        }
        else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int16> > *)0 ) ) )
        {
            uno::Sequence< uno::Sequence<sal_Int16> > aRowSeq;
            rArg >>= aRowSeq;

            long nStartRow = nDocRow;
            long nMaxColCount = 0;
            long nRowCount = aRowSeq.getLength();
            const uno::Sequence<sal_Int16>* pRowArr = aRowSeq.getConstArray();
            for (long nRow=0; nRow<nRowCount; nRow++)
            {
                long nColCount = pRowArr[nRow].getLength();
                if ( nColCount > nMaxColCount )
                    nMaxColCount = nColCount;
                const sal_Int16* pColArr = pRowArr[nRow].getConstArray();
                for (long nCol=0; nCol<nColCount; nCol++)
                    if ( nCol <= MAXCOL && nDocRow <= MAXROW )
                        pDoc->SetValue( (USHORT) nCol, (USHORT) nDocRow, 0, pColArr[nCol] );
                    else
                        bOverflow = TRUE;
                ++nDocRow;
            }

            if ( nRowCount && nMaxColCount && !bOverflow )
                lcl_AddRef( aTokenArr, nStartRow, nMaxColCount, nRowCount );
        }
        else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int32> > *)0 ) ) )
        {
            uno::Sequence< uno::Sequence<sal_Int32> > aRowSeq;
            rArg >>= aRowSeq;

            long nStartRow = nDocRow;
            long nMaxColCount = 0;
            long nRowCount = aRowSeq.getLength();
            const uno::Sequence<sal_Int32>* pRowArr = aRowSeq.getConstArray();
            for (long nRow=0; nRow<nRowCount; nRow++)
            {
                long nColCount = pRowArr[nRow].getLength();
                if ( nColCount > nMaxColCount )
                    nMaxColCount = nColCount;
                const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
                for (long nCol=0; nCol<nColCount; nCol++)
                    if ( nCol <= MAXCOL && nDocRow <= MAXROW )
                        pDoc->SetValue( (USHORT) nCol, (USHORT) nDocRow, 0, pColArr[nCol] );
                    else
                        bOverflow = TRUE;
                ++nDocRow;
            }

            if ( nRowCount && nMaxColCount && !bOverflow )
                lcl_AddRef( aTokenArr, nStartRow, nMaxColCount, nRowCount );
        }
        else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) )
        {
            uno::Sequence< uno::Sequence<double> > aRowSeq;
            rArg >>= aRowSeq;

            long nStartRow = nDocRow;
            long nMaxColCount = 0;
            long nRowCount = aRowSeq.getLength();
            const uno::Sequence<double>* pRowArr = aRowSeq.getConstArray();
            for (long nRow=0; nRow<nRowCount; nRow++)
            {
                long nColCount = pRowArr[nRow].getLength();
                if ( nColCount > nMaxColCount )
                    nMaxColCount = nColCount;
                const double* pColArr = pRowArr[nRow].getConstArray();
                for (long nCol=0; nCol<nColCount; nCol++)
                    if ( nCol <= MAXCOL && nDocRow <= MAXROW )
                        pDoc->SetValue( (USHORT) nCol, (USHORT) nDocRow, 0, pColArr[nCol] );
                    else
                        bOverflow = TRUE;
                ++nDocRow;
            }

            if ( nRowCount && nMaxColCount && !bOverflow )
                lcl_AddRef( aTokenArr, nStartRow, nMaxColCount, nRowCount );
        }
        else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence< ::rtl::OUString> > *)0 ) ) )
        {
            uno::Sequence< uno::Sequence< ::rtl::OUString> > aRowSeq;
            rArg >>= aRowSeq;

            long nStartRow = nDocRow;
            long nMaxColCount = 0;
            long nRowCount = aRowSeq.getLength();
            const uno::Sequence< ::rtl::OUString>* pRowArr = aRowSeq.getConstArray();
            for (long nRow=0; nRow<nRowCount; nRow++)
            {
                long nColCount = pRowArr[nRow].getLength();
                if ( nColCount > nMaxColCount )
                    nMaxColCount = nColCount;
                const ::rtl::OUString* pColArr = pRowArr[nRow].getConstArray();
                for (long nCol=0; nCol<nColCount; nCol++)
                    if ( nCol <= MAXCOL && nDocRow <= MAXROW )
                    {
                        if ( pColArr[nCol].getLength() )
                            pDoc->PutCell( (USHORT) nCol, (USHORT) nDocRow, 0,
                                            new ScStringCell( pColArr[nCol] ) );
                    }
                    else
                        bOverflow = TRUE;
                ++nDocRow;
            }

            if ( nRowCount && nMaxColCount && !bOverflow )
                lcl_AddRef( aTokenArr, nStartRow, nMaxColCount, nRowCount );
        }
        else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) )
        {
            uno::Sequence< uno::Sequence<uno::Any> > aRowSeq;
            rArg >>= aRowSeq;

            long nStartRow = nDocRow;
            long nMaxColCount = 0;
            long nRowCount = aRowSeq.getLength();
            const uno::Sequence<uno::Any>* pRowArr = aRowSeq.getConstArray();
            for (long nRow=0; nRow<nRowCount; nRow++)
            {
                long nColCount = pRowArr[nRow].getLength();
                if ( nColCount > nMaxColCount )
                    nMaxColCount = nColCount;
                const uno::Any* pColArr = pRowArr[nRow].getConstArray();
                for (long nCol=0; nCol<nColCount; nCol++)
                    if ( nCol <= MAXCOL && nDocRow <= MAXROW )
                    {
                        const uno::Any& rElement = pColArr[nCol];
                        uno::TypeClass eElemClass = rElement.getValueTypeClass();
                        if ( eElemClass == uno::TypeClass_VOID )
                        {
                            // leave empty
                        }
                        else if ( eElemClass == uno::TypeClass_BYTE ||
                                    eElemClass == uno::TypeClass_SHORT ||
                                    eElemClass == uno::TypeClass_UNSIGNED_SHORT ||
                                    eElemClass == uno::TypeClass_LONG ||
                                    eElemClass == uno::TypeClass_UNSIGNED_LONG ||
                                    eElemClass == uno::TypeClass_FLOAT ||
                                    eElemClass == uno::TypeClass_DOUBLE )
                        {
                            //  #87871# accept integer types because Basic passes a floating point
                            //  variable as byte, short or long if it's an integer number.
                            double fVal(0.0);
                            rElement >>= fVal;
                            pDoc->SetValue( (USHORT) nCol, (USHORT) nDocRow, 0, fVal );
                        }
                        else if ( eElemClass == uno::TypeClass_STRING )
                        {
                            ::rtl::OUString aUStr;
                            rElement >>= aUStr;
                            if ( aUStr.getLength() )
                                pDoc->PutCell( (USHORT) nCol, (USHORT) nDocRow, 0,
                                                            new ScStringCell( aUStr ) );
                        }
                        else
                            bArgErr = TRUE;     // invalid type
                    }
                    else
                        bOverflow = TRUE;
                ++nDocRow;
            }

            if ( nRowCount && nMaxColCount && !bOverflow )
                lcl_AddRef( aTokenArr, nStartRow, nMaxColCount, nRowCount );
        }
        else if ( aType.equals( getCppuType( (uno::Reference<table::XCellRange>*)0 ) ) )
        {
            // currently, only our own cell ranges are supported

            uno::Reference<table::XCellRange> xRange;
            rArg >>= xRange;
            ScCellRangesBase* pImpl = ScCellRangesBase::getImplementation( xRange );
            if ( pImpl )
            {
                ScDocument* pSrcDoc = pImpl->GetDocument();
                const ScRangeList& rRanges = pImpl->GetRangeList();
                if ( pSrcDoc && rRanges.Count() == 1 )
                {
                    ScRange aSrcRange = *rRanges.GetObject(0);

                    long nStartRow = nDocRow;
                    long nColCount = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
                    long nRowCount = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;

                    if ( nStartRow + nRowCount > MAXROW )
                        bOverflow = TRUE;
                    else
                    {
                        // copy data
                        if ( !lcl_CopyData( pSrcDoc, aSrcRange, pDoc, ScAddress( 0, (USHORT)nDocRow, 0 ) ) )
                            bOverflow = TRUE;
                    }

                    nDocRow += nRowCount;
                    if ( !bOverflow )
                        lcl_AddRef( aTokenArr, nStartRow, nColCount, nRowCount );
                }
                else
                    bArgErr = TRUE;
            }
            else
                bArgErr = TRUE;
        }
        else
            bArgErr = TRUE;                 // invalid type
    }
    aTokenArr.AddOpCode(ocClose);
    aTokenArr.AddOpCode(ocStop);

    //
    //  execute formula
    //

    uno::Any aRet;
    if ( !bArgErr && !bOverflow && nDocRow <= MAXROW )
    {
        ScAddress aFormulaPos( 0, (USHORT)nDocRow, 0 );
        ScFormulaCell* pFormula = new ScFormulaCell( pDoc, aFormulaPos, &aTokenArr, MM_FORMULA );
        pDoc->PutCell( aFormulaPos, pFormula );     //! necessary?

        //  call GetMatrix before GetErrCode because GetMatrix always recalculates
        //  if there is no matrix result

        ScMatrix* pMat = NULL;
        pFormula->GetMatrix(&pMat);
        USHORT nErrCode = pFormula->GetErrCode();
        if ( nErrCode == 0 )
        {
            if ( pMat )
            {
                // array result
                ScRangeToSequence::FillMixedArray( aRet, pMat );
            }
            else if ( pFormula->IsValue() )
            {
                // numeric value
                aRet <<= (double) pFormula->GetValue();
            }
            else
            {
                // string result
                String aStrVal;
                pFormula->GetString( aStrVal );
                aRet <<= ::rtl::OUString( aStrVal );
            }
        }
        else if ( nErrCode == NOVALUE )
        {
            // #N/A: leave result empty, no exception
        }
        else
        {
            //  any other error: IllegalArgumentException
            bArgErr = TRUE;
        }

        pDoc->DeleteAreaTab( 0, 0, MAXCOL, MAXROW, 0, IDF_ALL );
    }

    if (bOverflow)
        throw uno::RuntimeException();

    if (bArgErr)
        throw lang::IllegalArgumentException();

    return aRet;
}


}

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