/************************************************************************* * * $RCSfile: funcuno.cxx,v $ * * $Revision: 1.5 $ * * last change: $Author: nn $ $Date: 2000-12-21 13:59:04 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://www.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #ifdef PCH #include "ui_pch.hxx" #endif #pragma hdrstop #include #include #include #include "funcuno.hxx" #include "miscuno.hxx" #include "cellsuno.hxx" #include "unoguard.hxx" #include "scdll.hxx" #include "document.hxx" #include "compiler.hxx" #include "callform.hxx" #include "addincol.hxx" #include "rangeseq.hxx" #include "cell.hxx" #include "docoptio.hxx" #include "optuno.hxx" // for lcl_CopyData: #include "markdata.hxx" #include "patattr.hxx" #include "docpool.hxx" #include "attrib.hxx" 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() : bInvalid( FALSE ), pOptions( NULL ) { 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 SAL_CALL ScFunctionAccess_CreateInstance( const uno::Reference& ) { ScUnoGuard 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::createFromAscii( "stardiv.StarCalc.ScFunctionAccess" ); } uno::Sequence ScFunctionAccess::getSupportedServiceNames_Static() { uno::Sequence aRet(1); rtl::OUString* pArray = aRet.getArray(); pArray[0] = rtl::OUString::createFromAscii( SCFUNCTIONACCESS_SERVICE ); return aRet; } // XServiceInfo rtl::OUString SAL_CALL ScFunctionAccess::getImplementationName() throw(uno::RuntimeException) { return rtl::OUString::createFromAscii( "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 SAL_CALL ScFunctionAccess::getSupportedServiceNames() throw(uno::RuntimeException) { uno::Sequence aRet(2); rtl::OUString* pArray = aRet.getArray(); pArray[0] = rtl::OUString::createFromAscii( SCFUNCTIONACCESS_SERVICE ); pArray[1] = rtl::OUString::createFromAscii( SCDOCSETTINGS_SERVICE ); return aRet; } // XPropertySet (document settings) uno::Reference SAL_CALL ScFunctionAccess::getPropertySetInfo() throw(uno::RuntimeException) { ScUnoGuard aGuard; static uno::Reference 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) { ScUnoGuard 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) { ScUnoGuard 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( rName ); ScGlobal::pCharClass->toUpper( aUpper ); // same options as in ScCompiler::IsOpCode: // 1. built-in function name DBG_ASSERT( ScCompiler::pSymbolTableEnglish, "no symbol table" ); if (!ScCompiler::pSymbolTableEnglish) return FALSE; for ( USHORT nPos=0; nPosSearchFunc( 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& aArguments ) throw(container::NoSuchElementException, lang::IllegalArgumentException, uno::RuntimeException) { ScUnoGuard 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 0 ) aTokenArr.AddOpCode(ocSep); const uno::Any& rArg = pArgArr[nPos]; uno::TypeClass eClass = rArg.getValueTypeClass(); uno::Type aType = rArg.getValueType(); if ( eClass == uno::TypeClass_SHORT || eClass == uno::TypeClass_LONG || eClass == uno::TypeClass_DOUBLE ) { double fVal; 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 > *)0 ) ) ) { uno::Sequence< uno::Sequence > aRowSeq; rArg >>= aRowSeq; long nStartRow = nDocRow; long nMaxColCount = 0; long nRowCount = aRowSeq.getLength(); const uno::Sequence* pRowArr = aRowSeq.getConstArray(); for (long nRow=0; nRow nMaxColCount ) nMaxColCount = nColCount; const sal_Int16* pColArr = pRowArr[nRow].getConstArray(); for (long nCol=0; nColSetValue( (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 > *)0 ) ) ) { uno::Sequence< uno::Sequence > aRowSeq; rArg >>= aRowSeq; long nStartRow = nDocRow; long nMaxColCount = 0; long nRowCount = aRowSeq.getLength(); const uno::Sequence* pRowArr = aRowSeq.getConstArray(); for (long nRow=0; nRow nMaxColCount ) nMaxColCount = nColCount; const sal_Int32* pColArr = pRowArr[nRow].getConstArray(); for (long nCol=0; nColSetValue( (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 > *)0 ) ) ) { uno::Sequence< uno::Sequence > aRowSeq; rArg >>= aRowSeq; long nStartRow = nDocRow; long nMaxColCount = 0; long nRowCount = aRowSeq.getLength(); const uno::Sequence* pRowArr = aRowSeq.getConstArray(); for (long nRow=0; nRow nMaxColCount ) nMaxColCount = nColCount; const double* pColArr = pRowArr[nRow].getConstArray(); for (long nCol=0; nColSetValue( (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 > *)0 ) ) ) { uno::Sequence< uno::Sequence > aRowSeq; rArg >>= aRowSeq; long nStartRow = nDocRow; long nMaxColCount = 0; long nRowCount = aRowSeq.getLength(); const uno::Sequence* pRowArr = aRowSeq.getConstArray(); for (long nRow=0; nRow nMaxColCount ) nMaxColCount = nColCount; const rtl::OUString* pColArr = pRowArr[nRow].getConstArray(); for (long nCol=0; nColPutCell( (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 > *)0 ) ) ) { uno::Sequence< uno::Sequence > aRowSeq; rArg >>= aRowSeq; long nStartRow = nDocRow; long nMaxColCount = 0; long nRowCount = aRowSeq.getLength(); const uno::Sequence* pRowArr = aRowSeq.getConstArray(); for (long nRow=0; nRow nMaxColCount ) nMaxColCount = nColCount; const uno::Any* pColArr = pRowArr[nRow].getConstArray(); for (long nCol=0; nCol>= 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*)0 ) ) ) { // currently, only our own cell ranges are supported uno::Reference 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; }