Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / sc / source / ui / unoobj / funcuno.cxx
blobc5a2e4805c84e82a675f69d2017088c67f39fc52
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cppuhelper/supportsservice.hxx>
21 #include <sfx2/app.hxx>
22 #include <svl/itemprop.hxx>
23 #include <svl/sharedstringpool.hxx>
25 #include "scitems.hxx"
26 #include "funcuno.hxx"
27 #include "miscuno.hxx"
28 #include "cellsuno.hxx"
29 #include "scdll.hxx"
30 #include "document.hxx"
31 #include "compiler.hxx"
32 #include <formula/errorcodes.hxx>
33 #include "callform.hxx"
34 #include "addincol.hxx"
35 #include "rangeseq.hxx"
36 #include "formulacell.hxx"
37 #include "docoptio.hxx"
38 #include "optuno.hxx"
39 #include <docuno.hxx>
40 #include "markdata.hxx"
41 #include "patattr.hxx"
42 #include "docpool.hxx"
43 #include "attrib.hxx"
44 #include "clipparam.hxx"
45 #include "dociter.hxx"
46 #include "stringutil.hxx"
47 #include "tokenarray.hxx"
49 using namespace com::sun::star;
51 // registered as implementation for service FunctionAccess,
52 // also supports service SpreadsheetDocumentSettings (to set null date etc.)
54 #define SCFUNCTIONACCESS_SERVICE "com.sun.star.sheet.FunctionAccess"
55 #define SCDOCSETTINGS_SERVICE "com.sun.star.sheet.SpreadsheetDocumentSettings"
57 // helper to use cached document if not in use, temporary document otherwise
59 class ScTempDocSource
61 private:
62 ScTempDocCache& rCache;
63 ScDocument* pTempDoc;
65 static ScDocument* CreateDocument(); // create and initialize doc
67 public:
68 ScTempDocSource( ScTempDocCache& rDocCache );
69 ~ScTempDocSource();
71 ScDocument* GetDocument();
74 ScDocument* ScTempDocSource::CreateDocument()
76 ScDocument* pDoc = new ScDocument; // SCDOCMODE_DOCUMENT
77 pDoc->MakeTable( 0 );
78 return pDoc;
81 ScTempDocSource::ScTempDocSource( ScTempDocCache& rDocCache ) :
82 rCache( rDocCache ),
83 pTempDoc( NULL )
85 if ( rCache.IsInUse() )
86 pTempDoc = CreateDocument();
87 else
89 rCache.SetInUse( true );
90 if ( !rCache.GetDocument() )
91 rCache.SetDocument( CreateDocument() );
95 ScTempDocSource::~ScTempDocSource()
97 if ( pTempDoc )
98 delete pTempDoc;
99 else
100 rCache.SetInUse( false );
103 ScDocument* ScTempDocSource::GetDocument()
105 if ( pTempDoc )
106 return pTempDoc;
107 else
108 return rCache.GetDocument();
111 ScTempDocCache::ScTempDocCache() :
112 pDoc( NULL ),
113 bInUse( false )
117 ScTempDocCache::~ScTempDocCache()
119 OSL_ENSURE( !bInUse, "ScTempDocCache dtor: bInUse" );
120 delete pDoc;
123 void ScTempDocCache::SetDocument( ScDocument* pNew )
125 OSL_ENSURE( !pDoc, "ScTempDocCache::SetDocument: already set" );
126 pDoc = pNew;
129 void ScTempDocCache::Clear()
131 OSL_ENSURE( !bInUse, "ScTempDocCache::Clear: bInUse" );
132 delete pDoc;
133 pDoc = NULL;
136 // copy results from one document into another
137 //! merge this with ScAreaLink::Refresh
138 //! copy directly without a clipboard document?
140 static bool lcl_CopyData( ScDocument* pSrcDoc, const ScRange& rSrcRange,
141 ScDocument* pDestDoc, const ScAddress& rDestPos )
143 SCTAB nSrcTab = rSrcRange.aStart.Tab();
144 SCTAB nDestTab = rDestPos.Tab();
146 ScRange aNewRange( rDestPos, ScAddress(
147 rSrcRange.aEnd.Col() - rSrcRange.aStart.Col() + rDestPos.Col(),
148 rSrcRange.aEnd.Row() - rSrcRange.aStart.Row() + rDestPos.Row(),
149 nDestTab ) );
151 ScDocument* pClipDoc = new ScDocument( SCDOCMODE_CLIP );
152 ScMarkData aSourceMark;
153 aSourceMark.SelectOneTable( nSrcTab ); // for CopyToClip
154 aSourceMark.SetMarkArea( rSrcRange );
155 ScClipParam aClipParam(rSrcRange, false);
156 pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aSourceMark, false);
158 if ( pClipDoc->HasAttrib( 0,0,nSrcTab, MAXCOL,MAXROW,nSrcTab,
159 HASATTR_MERGED | HASATTR_OVERLAPPED ) )
161 ScPatternAttr aPattern( pSrcDoc->GetPool() );
162 aPattern.GetItemSet().Put( ScMergeAttr() ); // Defaults
163 aPattern.GetItemSet().Put( ScMergeFlagAttr() );
164 pClipDoc->ApplyPatternAreaTab( 0,0, MAXCOL,MAXROW, nSrcTab, aPattern );
167 ScMarkData aDestMark;
168 aDestMark.SelectOneTable( nDestTab );
169 aDestMark.SetMarkArea( aNewRange );
170 pDestDoc->CopyFromClip( aNewRange, aDestMark, IDF_ALL & ~IDF_FORMULA, NULL, pClipDoc, false );
172 delete pClipDoc;
173 return true;
176 ScFunctionAccess::ScFunctionAccess() :
177 pOptions( NULL ),
178 aPropertyMap( ScDocOptionsHelper::GetPropertyMap() ),
179 mbArray( true ), // default according to behaviour of older Office versions
180 mbValid( true )
182 StartListening( *SFX_APP() ); // for SFX_HINT_DEINITIALIZING
185 ScFunctionAccess::~ScFunctionAccess()
187 delete pOptions;
190 void ScFunctionAccess::Notify( SfxBroadcaster&, const SfxHint& rHint )
192 if ( rHint.ISA(SfxSimpleHint) &&
193 ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_DEINITIALIZING )
195 // document must not be used anymore
196 aDocCache.Clear();
197 mbValid = false;
201 // stuff for exService_...
203 uno::Reference<uno::XInterface> SAL_CALL ScFunctionAccess_CreateInstance(
204 const uno::Reference<lang::XMultiServiceFactory>& )
206 SolarMutexGuard aGuard;
207 ScDLL::Init();
208 static uno::Reference< uno::XInterface > xInst((::cppu::OWeakObject*) new ScFunctionAccess);
209 return xInst;
212 OUString ScFunctionAccess::getImplementationName_Static()
214 return OUString( "stardiv.StarCalc.ScFunctionAccess" );
217 uno::Sequence<OUString> ScFunctionAccess::getSupportedServiceNames_Static()
219 uno::Sequence<OUString> aRet(1);
220 OUString* pArray = aRet.getArray();
221 pArray[0] = OUString( SCFUNCTIONACCESS_SERVICE );
222 return aRet;
225 // XServiceInfo
226 OUString SAL_CALL ScFunctionAccess::getImplementationName() throw(uno::RuntimeException, std::exception)
228 return OUString( "ScFunctionAccess");
231 sal_Bool SAL_CALL ScFunctionAccess::supportsService( const OUString& rServiceName )
232 throw(uno::RuntimeException, std::exception)
234 return cppu::supportsService(this, rServiceName);
237 uno::Sequence<OUString> SAL_CALL ScFunctionAccess::getSupportedServiceNames()
238 throw(uno::RuntimeException, std::exception)
240 uno::Sequence<OUString> aRet(2);
241 OUString* pArray = aRet.getArray();
242 pArray[0] = OUString( SCFUNCTIONACCESS_SERVICE );
243 pArray[1] = OUString( SCDOCSETTINGS_SERVICE );
244 return aRet;
247 // XPropertySet (document settings)
249 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScFunctionAccess::getPropertySetInfo()
250 throw(uno::RuntimeException, std::exception)
252 SolarMutexGuard aGuard;
253 static uno::Reference<beans::XPropertySetInfo> aRef(
254 new SfxItemPropertySetInfo( aPropertyMap ));
255 return aRef;
258 void SAL_CALL ScFunctionAccess::setPropertyValue(
259 const OUString& aPropertyName, const uno::Any& aValue )
260 throw(beans::UnknownPropertyException, beans::PropertyVetoException,
261 lang::IllegalArgumentException, lang::WrappedTargetException,
262 uno::RuntimeException, std::exception)
264 SolarMutexGuard aGuard;
266 if ( aPropertyName == "IsArrayFunction" )
268 if( !(aValue >>= mbArray) )
269 throw lang::IllegalArgumentException();
271 else
273 if ( !pOptions )
274 pOptions = new ScDocOptions();
276 // options aren't initialized from configuration - always get the same default behaviour
278 bool bDone = ScDocOptionsHelper::setPropertyValue( *pOptions, aPropertyMap, aPropertyName, aValue );
279 if (!bDone)
280 throw beans::UnknownPropertyException();
284 uno::Any SAL_CALL ScFunctionAccess::getPropertyValue( const OUString& aPropertyName )
285 throw(beans::UnknownPropertyException, lang::WrappedTargetException,
286 uno::RuntimeException, std::exception)
288 SolarMutexGuard aGuard;
290 if ( aPropertyName == "IsArrayFunction" )
291 return uno::Any( mbArray );
293 if ( !pOptions )
294 pOptions = new ScDocOptions();
296 // options aren't initialized from configuration - always get the same default behaviour
298 return ScDocOptionsHelper::getPropertyValue( *pOptions, aPropertyMap, aPropertyName );
301 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess )
303 // XFunctionAccess
305 static bool lcl_AddFunctionToken( ScTokenArray& rArray, const OUString& rName,const ScCompiler& rCompiler )
307 // function names are always case-insensitive
308 OUString aUpper = ScGlobal::pCharClass->uppercase(rName);
310 // same options as in ScCompiler::IsOpCode:
311 // 1. built-in function name
313 OpCode eOp = rCompiler.GetEnglishOpCode( aUpper );
314 if ( eOp != ocNone )
316 rArray.AddOpCode( eOp );
317 return true;
320 // 2. old add in functions
322 if (ScGlobal::GetFuncCollection()->findByName(aUpper))
324 rArray.AddExternal(aUpper.getStr());
325 return true;
328 // 3. new (uno) add in functions
330 OUString aIntName =
331 ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
332 if (!aIntName.isEmpty())
334 rArray.AddExternal(aIntName.getStr()); // international name
335 return true;
338 return false; // no valid function name
341 static void lcl_AddRef( ScTokenArray& rArray, long nStartRow, long nColCount, long nRowCount )
343 ScComplexRefData aRef;
344 aRef.InitRange(ScRange(0,nStartRow,0,nColCount-1,nStartRow+nRowCount-1,0));
345 rArray.AddDoubleReference(aRef);
348 class SimpleVisitor
350 protected:
351 bool mbArgError;
352 ScDocument* mpDoc;
353 public:
354 SimpleVisitor( ScDocument* pDoc ) : mbArgError( false ), mpDoc( pDoc ) {}
355 // could possibly just get away with JUST the following overload
356 // 1) virtual void visitElem( long& nCol, long& nRow, const double& elem )
357 // 2) virtual void visitElem( long& nCol, long& nRow, const OUString& elem )
358 // 3) virtual void visitElem( long& nCol, long& nRow, const uno::Any& elem )
359 // the other types methods are here just to reflect the orig code and for
360 // completeness.
362 void visitElem( long nCol, long nRow, const sal_Int16& elem )
364 mpDoc->SetValue( (SCCOL) nCol, (SCROW) nRow, 0, elem );
366 void visitElem( long nCol, long nRow, const sal_Int32& elem )
368 mpDoc->SetValue( (SCCOL) nCol, (SCROW) nRow, 0, elem );
370 void visitElem( long nCol, long nRow, const double& elem )
372 mpDoc->SetValue( (SCCOL) nCol, (SCROW) nRow, 0, elem );
374 void visitElem( long nCol, long nRow, const OUString& elem )
376 if (!elem.isEmpty())
378 ScSetStringParam aParam;
379 aParam.setTextInput();
380 mpDoc->SetString(ScAddress(nCol,nRow,0), elem, &aParam);
383 void visitElem( long nCol, long nRow, const uno::Any& rElement )
385 uno::TypeClass eElemClass = rElement.getValueTypeClass();
386 if ( eElemClass == uno::TypeClass_VOID )
388 // leave empty
390 else if ( eElemClass == uno::TypeClass_BYTE ||
391 eElemClass == uno::TypeClass_SHORT ||
392 eElemClass == uno::TypeClass_UNSIGNED_SHORT ||
393 eElemClass == uno::TypeClass_LONG ||
394 eElemClass == uno::TypeClass_UNSIGNED_LONG ||
395 eElemClass == uno::TypeClass_FLOAT ||
396 eElemClass == uno::TypeClass_DOUBLE )
398 // accept integer types because Basic passes a floating point
399 // variable as byte, short or long if it's an integer number.
400 double fVal(0.0);
401 rElement >>= fVal;
402 visitElem( nCol, nRow, fVal );
404 else if ( eElemClass == uno::TypeClass_STRING )
406 OUString aUStr;
407 rElement >>= aUStr;
408 visitElem( nCol, nRow, aUStr );
410 else
411 mbArgError = true;
413 bool hasArgError() const { return mbArgError; }
416 template< class seq >
417 class SequencesContainer
419 uno::Sequence< uno::Sequence< seq > > maSeq;
421 long& mrDocRow;
422 bool mbOverflow;
423 bool mbArgError;
424 ScDocument* mpDoc;
425 ScTokenArray& mrTokenArr;
427 public:
428 SequencesContainer( const uno::Any& rArg, ScTokenArray& rTokenArr, long& rDocRow, ScDocument* pDoc ) :
429 mrDocRow( rDocRow ), mbOverflow(false), mbArgError(false), mpDoc( pDoc ), mrTokenArr( rTokenArr )
431 rArg >>= maSeq;
434 void process()
436 SimpleVisitor aVisitor(mpDoc);
437 long nStartRow = mrDocRow;
438 long nRowCount = maSeq.getLength();
439 long nMaxColCount = 0;
440 const uno::Sequence< seq >* pRowArr = maSeq.getConstArray();
441 for ( long nRow=0; nRow<nRowCount; nRow++ )
443 long nColCount = pRowArr[nRow].getLength();
444 if ( nColCount > nMaxColCount )
445 nMaxColCount = nColCount;
446 const seq* pColArr = pRowArr[nRow].getConstArray();
447 for (long nCol=0; nCol<nColCount; nCol++)
448 if ( nCol <= MAXCOL && mrDocRow <= MAXROW )
449 aVisitor.visitElem( nCol, mrDocRow, pColArr[ nCol ] );
450 else
451 mbOverflow=true;
452 mrDocRow++;
454 mbArgError = aVisitor.hasArgError();
455 if ( nRowCount && nMaxColCount && !mbOverflow )
456 lcl_AddRef( mrTokenArr, nStartRow, nMaxColCount, nRowCount );
458 bool getOverflow() const { return mbOverflow; }
459 bool getArgError() const { return mbArgError; }
462 template <class T>
463 class ArrayOfArrayProc
465 public:
466 static void processSequences( ScDocument* pDoc, const uno::Any& rArg, ScTokenArray& rTokenArr,
467 long& rDocRow, bool& rArgErr, bool& rOverflow )
469 SequencesContainer< T > aContainer( rArg, rTokenArr, rDocRow, pDoc );
470 aContainer.process();
471 rArgErr = aContainer.getArgError();
472 rOverflow = aContainer.getOverflow();
476 uno::Any SAL_CALL ScFunctionAccess::callFunction( const OUString& aName,
477 const uno::Sequence<uno::Any>& aArguments )
478 throw (container::NoSuchElementException, lang::IllegalArgumentException,
479 uno::RuntimeException, std::exception)
481 SolarMutexGuard aGuard;
483 if (!mbValid)
484 throw uno::RuntimeException();
486 // use cached document if not in use, temporary document otherwise
487 // (deleted in ScTempDocSource dtor)
488 ScTempDocSource aSource( aDocCache );
489 ScDocument* pDoc = aSource.GetDocument();
490 const static SCTAB nTempSheet = 1;
491 // Create an extra tab to contain the Function Cell
492 // this will allow full rows to be used.
493 if ( !pDoc->HasTable( nTempSheet ) )
494 pDoc->MakeTable( nTempSheet );
496 /// TODO: check
497 ScAddress aAdr;
498 ScCompiler aCompiler(pDoc,aAdr);
499 aCompiler.SetGrammar(pDoc->GetGrammar());
502 // find function
505 ScTokenArray aTokenArr;
506 if ( !lcl_AddFunctionToken( aTokenArr, aName,aCompiler ) )
508 // function not found
509 throw container::NoSuchElementException();
513 // set options (null date, etc.)
516 if ( pOptions )
517 pDoc->SetDocOptions( *pOptions );
520 // add arguments to token array
523 bool bArgErr = false;
524 bool bOverflow = false;
525 long nDocRow = 0;
526 long nArgCount = aArguments.getLength();
527 const uno::Any* pArgArr = aArguments.getConstArray();
529 svl::SharedStringPool& rSPool = pDoc->GetSharedStringPool();
530 aTokenArr.AddOpCode(ocOpen);
531 for (long nPos=0; nPos<nArgCount; nPos++)
533 if ( nPos > 0 )
534 aTokenArr.AddOpCode(ocSep);
536 const uno::Any& rArg = pArgArr[nPos];
538 uno::TypeClass eClass = rArg.getValueTypeClass();
539 uno::Type aType = rArg.getValueType();
540 if ( eClass == uno::TypeClass_BYTE ||
541 eClass == uno::TypeClass_BOOLEAN ||
542 eClass == uno::TypeClass_SHORT ||
543 eClass == uno::TypeClass_UNSIGNED_SHORT ||
544 eClass == uno::TypeClass_LONG ||
545 eClass == uno::TypeClass_UNSIGNED_LONG ||
546 eClass == uno::TypeClass_FLOAT ||
547 eClass == uno::TypeClass_DOUBLE )
549 // accept integer types because Basic passes a floating point
550 // variable as byte, short or long if it's an integer number.
551 double fVal = 0;
552 rArg >>= fVal;
553 aTokenArr.AddDouble( fVal );
555 else if ( eClass == uno::TypeClass_STRING )
557 OUString aUStr;
558 rArg >>= aUStr;
559 aTokenArr.AddString(rSPool.intern(aUStr));
561 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int16> > *)0 ) ) )
563 ArrayOfArrayProc<sal_Int16>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
565 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int32> > *)0 ) ) )
567 ArrayOfArrayProc<sal_Int32>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
569 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) )
571 ArrayOfArrayProc<double>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
573 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<OUString> > *)0 ) ) )
575 ArrayOfArrayProc<OUString>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
577 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) )
579 ArrayOfArrayProc<uno::Any>::processSequences( pDoc, rArg, aTokenArr, nDocRow, bArgErr, bOverflow );
581 else if ( aType.equals( cppu::UnoType<table::XCellRange>::get()) )
583 // currently, only our own cell ranges are supported
585 uno::Reference<table::XCellRange> xRange(rArg, uno::UNO_QUERY);
586 ScCellRangesBase* pImpl = ScCellRangesBase::getImplementation( xRange );
587 if ( pImpl )
589 ScDocument* pSrcDoc = pImpl->GetDocument();
590 const ScRangeList& rRanges = pImpl->GetRangeList();
591 if ( pSrcDoc && rRanges.size() == 1 )
593 ScRange aSrcRange = *rRanges[ 0 ];
595 long nStartRow = nDocRow;
596 long nColCount = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1;
597 long nRowCount = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1;
599 if ( nStartRow + nRowCount > MAXROWCOUNT )
600 bOverflow = true;
601 else
603 // copy data
604 if ( !lcl_CopyData( pSrcDoc, aSrcRange, pDoc, ScAddress( 0, (SCROW)nDocRow, 0 ) ) )
605 bOverflow = true;
608 nDocRow += nRowCount;
609 if ( !bOverflow )
610 lcl_AddRef( aTokenArr, nStartRow, nColCount, nRowCount );
612 else
613 bArgErr = true;
615 else
616 bArgErr = true;
618 else
619 bArgErr = true; // invalid type
621 aTokenArr.AddOpCode(ocClose);
622 aTokenArr.AddOpCode(ocStop);
625 // execute formula
628 uno::Any aRet;
629 if ( !bArgErr && !bOverflow && nDocRow <= MAXROWCOUNT )
631 ScAddress aFormulaPos( 0, 0, nTempSheet );
632 // GRAM_PODF_A1 doesn't really matter for the token array but fits with
633 // other API compatibility grammars.
634 ScFormulaCell* pFormula = new ScFormulaCell(
635 pDoc, aFormulaPos, aTokenArr, formula::FormulaGrammar::GRAM_PODF_A1,
636 (sal_uInt8)(mbArray ? MM_FORMULA : MM_NONE) );
637 pFormula = pDoc->SetFormulaCell(aFormulaPos, pFormula);
639 // call GetMatrix before GetErrCode because GetMatrix always recalculates
640 // if there is no matrix result
642 const ScMatrix* pMat = (mbArray && pFormula) ? pFormula->GetMatrix() : 0;
643 sal_uInt16 nErrCode = pFormula ? pFormula->GetErrCode() : errIllegalArgument;
644 if ( nErrCode == 0 )
646 if ( pMat )
648 // array result
649 ScRangeToSequence::FillMixedArray( aRet, pMat );
651 else if ( pFormula->IsValue() )
653 // numeric value
654 aRet <<= (double) pFormula->GetValue();
656 else
658 // string result
659 OUString aStrVal = pFormula->GetString().getString();
660 aRet <<= aStrVal;
663 else if ( nErrCode == NOTAVAILABLE )
665 // #N/A: leave result empty, no exception
667 else
669 // any other error: IllegalArgumentException
670 bArgErr = true;
673 pDoc->DeleteAreaTab( 0, 0, MAXCOL, MAXROW, 0, IDF_ALL );
674 pDoc->DeleteAreaTab( 0, 0, 0, 0, nTempSheet, IDF_ALL );
677 if (bOverflow)
678 throw uno::RuntimeException();
680 if (bArgErr)
681 throw lang::IllegalArgumentException();
683 return aRet;
687 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */