1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: funcuno.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 #include <tools/debug.hxx>
37 #include <sfx2/app.hxx>
38 #include <svtools/itemprop.hxx>
40 #include "funcuno.hxx"
41 #include "miscuno.hxx"
42 #include "cellsuno.hxx"
43 #include "unoguard.hxx"
45 #include "document.hxx"
46 #include "compiler.hxx"
47 #include "formula/errorcodes.hxx"
48 #include "callform.hxx"
49 #include "addincol.hxx"
50 #include "rangeseq.hxx"
52 #include "docoptio.hxx"
56 #include "markdata.hxx"
57 #include "patattr.hxx"
58 #include "docpool.hxx"
60 #include "clipparam.hxx"
62 using namespace com::sun::star
;
64 //------------------------------------------------------------------------
66 // registered as implementation for service FunctionAccess,
67 // also supports service SpreadsheetDocumentSettings (to set null date etc.)
69 #define SCFUNCTIONACCESS_SERVICE "com.sun.star.sheet.FunctionAccess"
70 #define SCDOCSETTINGS_SERVICE "com.sun.star.sheet.SpreadsheetDocumentSettings"
72 //------------------------------------------------------------------------
74 // helper to use cached document if not in use, temporary document otherwise
79 ScTempDocCache
& rCache
;
82 static ScDocument
* CreateDocument(); // create and initialize doc
85 ScTempDocSource( ScTempDocCache
& rDocCache
);
88 ScDocument
* GetDocument();
91 //------------------------------------------------------------------------
94 ScDocument
* ScTempDocSource::CreateDocument()
96 ScDocument
* pDoc
= new ScDocument
; // SCDOCMODE_DOCUMENT
101 ScTempDocSource::ScTempDocSource( ScTempDocCache
& rDocCache
) :
105 if ( rCache
.IsInUse() )
106 pTempDoc
= CreateDocument();
109 rCache
.SetInUse( TRUE
);
110 if ( !rCache
.GetDocument() )
111 rCache
.SetDocument( CreateDocument() );
115 ScTempDocSource::~ScTempDocSource()
120 rCache
.SetInUse( FALSE
);
123 ScDocument
* ScTempDocSource::GetDocument()
128 return rCache
.GetDocument();
131 //------------------------------------------------------------------------
133 ScTempDocCache::ScTempDocCache() :
139 ScTempDocCache::~ScTempDocCache()
141 DBG_ASSERT( !bInUse
, "ScTempDocCache dtor: bInUse" );
145 void ScTempDocCache::SetDocument( ScDocument
* pNew
)
147 DBG_ASSERT( !pDoc
, "ScTempDocCache::SetDocument: already set" );
151 void ScTempDocCache::Clear()
153 DBG_ASSERT( !bInUse
, "ScTempDocCache::Clear: bInUse" );
158 //------------------------------------------------------------------------
160 // copy results from one document into another
161 //! merge this with ScAreaLink::Refresh
162 //! copy directly without a clipboard document?
164 BOOL
lcl_CopyData( ScDocument
* pSrcDoc
, const ScRange
& rSrcRange
,
165 ScDocument
* pDestDoc
, const ScAddress
& rDestPos
)
167 SCTAB nSrcTab
= rSrcRange
.aStart
.Tab();
168 SCTAB nDestTab
= rDestPos
.Tab();
170 ScRange
aNewRange( rDestPos
, ScAddress(
171 rSrcRange
.aEnd
.Col() - rSrcRange
.aStart
.Col() + rDestPos
.Col(),
172 rSrcRange
.aEnd
.Row() - rSrcRange
.aStart
.Row() + rDestPos
.Row(),
175 ScDocument
* pClipDoc
= new ScDocument( SCDOCMODE_CLIP
);
176 ScMarkData aSourceMark
;
177 aSourceMark
.SelectOneTable( nSrcTab
); // for CopyToClip
178 aSourceMark
.SetMarkArea( rSrcRange
);
179 ScClipParam
aClipParam(rSrcRange
, false);
180 pSrcDoc
->CopyToClip(aClipParam
, pClipDoc
, &aSourceMark
, false);
182 if ( pClipDoc
->HasAttrib( 0,0,nSrcTab
, MAXCOL
,MAXROW
,nSrcTab
,
183 HASATTR_MERGED
| HASATTR_OVERLAPPED
) )
185 ScPatternAttr
aPattern( pSrcDoc
->GetPool() );
186 aPattern
.GetItemSet().Put( ScMergeAttr() ); // Defaults
187 aPattern
.GetItemSet().Put( ScMergeFlagAttr() );
188 pClipDoc
->ApplyPatternAreaTab( 0,0, MAXCOL
,MAXROW
, nSrcTab
, aPattern
);
191 ScMarkData aDestMark
;
192 aDestMark
.SelectOneTable( nDestTab
);
193 aDestMark
.SetMarkArea( aNewRange
);
194 pDestDoc
->CopyFromClip( aNewRange
, aDestMark
, IDF_ALL
& ~IDF_FORMULA
, NULL
, pClipDoc
, FALSE
);
200 //------------------------------------------------------------------------
202 ScFunctionAccess::ScFunctionAccess() :
204 aPropertyMap( ScDocOptionsHelper::GetPropertyMap() ),
207 StartListening( *SFX_APP() ); // for SFX_HINT_DEINITIALIZING
210 ScFunctionAccess::~ScFunctionAccess()
215 void ScFunctionAccess::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
217 if ( rHint
.ISA(SfxSimpleHint
) &&
218 ((SfxSimpleHint
&)rHint
).GetId() == SFX_HINT_DEINITIALIZING
)
220 // document must not be used anymore
226 // stuff for exService_...
228 uno::Reference
<uno::XInterface
> SAL_CALL
ScFunctionAccess_CreateInstance(
229 const uno::Reference
<lang::XMultiServiceFactory
>& )
233 static uno::Reference
< uno::XInterface
> xInst((::cppu::OWeakObject
*) new ScFunctionAccess
);
237 rtl::OUString
ScFunctionAccess::getImplementationName_Static()
239 return rtl::OUString::createFromAscii( "stardiv.StarCalc.ScFunctionAccess" );
242 uno::Sequence
<rtl::OUString
> ScFunctionAccess::getSupportedServiceNames_Static()
244 uno::Sequence
<rtl::OUString
> aRet(1);
245 rtl::OUString
* pArray
= aRet
.getArray();
246 pArray
[0] = rtl::OUString::createFromAscii( SCFUNCTIONACCESS_SERVICE
);
252 rtl::OUString SAL_CALL
ScFunctionAccess::getImplementationName() throw(uno::RuntimeException
)
254 return rtl::OUString::createFromAscii( "ScFunctionAccess" );
257 sal_Bool SAL_CALL
ScFunctionAccess::supportsService( const rtl::OUString
& rServiceName
)
258 throw(uno::RuntimeException
)
260 String
aServiceStr(rServiceName
);
261 return aServiceStr
.EqualsAscii( SCFUNCTIONACCESS_SERVICE
) ||
262 aServiceStr
.EqualsAscii( SCDOCSETTINGS_SERVICE
);
265 uno::Sequence
<rtl::OUString
> SAL_CALL
ScFunctionAccess::getSupportedServiceNames()
266 throw(uno::RuntimeException
)
268 uno::Sequence
<rtl::OUString
> aRet(2);
269 rtl::OUString
* pArray
= aRet
.getArray();
270 pArray
[0] = rtl::OUString::createFromAscii( SCFUNCTIONACCESS_SERVICE
);
271 pArray
[1] = rtl::OUString::createFromAscii( SCDOCSETTINGS_SERVICE
);
275 // XPropertySet (document settings)
277 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
ScFunctionAccess::getPropertySetInfo()
278 throw(uno::RuntimeException
)
281 static uno::Reference
<beans::XPropertySetInfo
> aRef(
282 new SfxItemPropertySetInfo( &aPropertyMap
));
286 void SAL_CALL
ScFunctionAccess::setPropertyValue(
287 const rtl::OUString
& aPropertyName
, const uno::Any
& aValue
)
288 throw(beans::UnknownPropertyException
, beans::PropertyVetoException
,
289 lang::IllegalArgumentException
, lang::WrappedTargetException
,
290 uno::RuntimeException
)
295 pOptions
= new ScDocOptions();
297 // options aren't initialized from configuration - always get the same default behaviour
299 BOOL bDone
= ScDocOptionsHelper::setPropertyValue( *pOptions
, aPropertyMap
, aPropertyName
, aValue
);
301 throw beans::UnknownPropertyException();
304 uno::Any SAL_CALL
ScFunctionAccess::getPropertyValue( const rtl::OUString
& aPropertyName
)
305 throw(beans::UnknownPropertyException
, lang::WrappedTargetException
,
306 uno::RuntimeException
)
311 pOptions
= new ScDocOptions();
313 // options aren't initialized from configuration - always get the same default behaviour
315 return ScDocOptionsHelper::getPropertyValue( *pOptions
, aPropertyMap
, aPropertyName
);
318 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess
)
322 BOOL
lcl_AddFunctionToken( ScTokenArray
& rArray
, const rtl::OUString
& rName
,const ScCompiler
& rCompiler
)
324 // function names are always case-insensitive
325 String
aUpper( ScGlobal::pCharClass
->upper( rName
) );
327 // same options as in ScCompiler::IsOpCode:
328 // 1. built-in function name
330 OpCode eOp
= rCompiler
.GetEnglishOpCode( aUpper
);
333 rArray
.AddOpCode( eOp
);
337 // 2. old add in functions
340 if ( ScGlobal::GetFuncCollection()->SearchFunc( aUpper
, nIndex
) )
342 rArray
.AddExternal( aUpper
.GetBuffer() );
346 // 3. new (uno) add in functions
348 String
aIntName(ScGlobal::GetAddInCollection()->FindFunction( aUpper
, FALSE
));
351 rArray
.AddExternal( aIntName
.GetBuffer() ); // international name
355 return FALSE
; // no valid function name
358 void lcl_AddRef( ScTokenArray
& rArray
, long nStartRow
, long nColCount
, long nRowCount
)
360 ScComplexRefData aRef
;
365 aRef
.Ref1
.nRow
= (SCROW
) nStartRow
;
366 aRef
.Ref2
.nCol
= (SCCOL
) (nColCount
- 1);
367 aRef
.Ref2
.nRow
= (SCROW
) (nStartRow
+ nRowCount
- 1);
368 rArray
.AddDoubleReference(aRef
);
377 SimpleVisitor( ScDocument
* pDoc
) : mbArgError( false ), mpDoc( pDoc
) {}
378 // could possibly just get away with JUST the following overload
379 // 1) virtual void visitElem( long& nCol, long& nRow, const double& elem )
380 // 2) virtual void visitElem( long& nCol, long& nRow, const rtl::OUString& elem )
381 // 3) virtual void visitElem( long& nCol, long& nRow, const uno::Any& elem )
382 // the other types methods are here just to reflect the orig code and for
385 void visitElem( long nCol
, long nRow
, const sal_Int16
& elem
)
387 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
389 void visitElem( long nCol
, long nRow
, const sal_Int32
& elem
)
391 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
393 void visitElem( long nCol
, long nRow
, const double& elem
)
395 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
397 void visitElem( long nCol
, long nRow
, const rtl::OUString
& elem
)
399 if ( elem
.getLength() )
400 mpDoc
->PutCell( (SCCOL
) nCol
, (SCROW
) nRow
, 0,
401 new ScStringCell( elem
) );
403 void visitElem( long nCol
, long nRow
, const uno::Any
& rElement
)
405 uno::TypeClass eElemClass
= rElement
.getValueTypeClass();
406 if ( eElemClass
== uno::TypeClass_VOID
)
410 else if ( eElemClass
== uno::TypeClass_BYTE
||
411 eElemClass
== uno::TypeClass_SHORT
||
412 eElemClass
== uno::TypeClass_UNSIGNED_SHORT
||
413 eElemClass
== uno::TypeClass_LONG
||
414 eElemClass
== uno::TypeClass_UNSIGNED_LONG
||
415 eElemClass
== uno::TypeClass_FLOAT
||
416 eElemClass
== uno::TypeClass_DOUBLE
)
418 // #87871# accept integer types because Basic passes a floating point
419 // variable as byte, short or long if it's an integer number.
422 visitElem( nCol
, nRow
, fVal
);
424 else if ( eElemClass
== uno::TypeClass_STRING
)
428 visitElem( nCol
, nRow
, aUStr
);
433 bool hasArgError() { return mbArgError
; }
436 template< class seq
>
437 class SequencesContainer
439 uno::Sequence
< uno::Sequence
< seq
> > maSeq
;
445 ScTokenArray
& mrTokenArr
;
448 SequencesContainer( const uno::Any
& rArg
, ScTokenArray
& rTokenArr
, long& rDocRow
, ScDocument
* pDoc
) :
449 mrDocRow( rDocRow
), mbOverflow(false), mbArgError(false), mpDoc( pDoc
), mrTokenArr( rTokenArr
)
456 SimpleVisitor
aVisitor(mpDoc
);
457 long nStartRow
= mrDocRow
;
458 long nRowCount
= maSeq
.getLength();
459 long nMaxColCount
= 0;
460 const uno::Sequence
< seq
>* pRowArr
= maSeq
.getConstArray();
461 for ( long nRow
=0; nRow
<nRowCount
; nRow
++ )
463 long nColCount
= pRowArr
[nRow
].getLength();
464 if ( nColCount
> nMaxColCount
)
465 nMaxColCount
= nColCount
;
466 const seq
* pColArr
= pRowArr
[nRow
].getConstArray();
467 for (long nCol
=0; nCol
<nColCount
; nCol
++)
468 if ( nCol
<= MAXCOL
&& mrDocRow
<= MAXROW
)
469 aVisitor
.visitElem( nCol
, mrDocRow
, pColArr
[ nCol
] );
474 mbArgError
= aVisitor
.hasArgError();
475 if ( nRowCount
&& nMaxColCount
&& !mbOverflow
)
476 lcl_AddRef( mrTokenArr
, nStartRow
, nMaxColCount
, nRowCount
);
478 bool getOverflow() { return mbOverflow
; }
479 bool getArgError() { return mbArgError
; }
483 class ArrayOfArrayProc
486 static void processSequences( ScDocument
* pDoc
, const uno::Any
& rArg
, ScTokenArray
& rTokenArr
,
487 long& rDocRow
, BOOL
& rArgErr
, BOOL
& rOverflow
)
489 SequencesContainer
< T
> aContainer( rArg
, rTokenArr
, rDocRow
, pDoc
);
490 aContainer
.process();
491 rArgErr
= aContainer
.getArgError();
492 rOverflow
= aContainer
.getOverflow();
496 uno::Any SAL_CALL
ScFunctionAccess::callFunction( const rtl::OUString
& aName
,
497 const uno::Sequence
<uno::Any
>& aArguments
)
498 throw(container::NoSuchElementException
, lang::IllegalArgumentException
,
499 uno::RuntimeException
)
504 throw uno::RuntimeException();
506 // use cached document if not in use, temporary document otherwise
507 // (deleted in ScTempDocSource dtor)
508 ScTempDocSource
aSource( aDocCache
);
509 ScDocument
* pDoc
= aSource
.GetDocument();
510 const static SCTAB nTempSheet
= 1;
511 // Create an extra tab to contain the Function Cell
512 // this will allow full rows to be used.
513 if ( !pDoc
->HasTable( nTempSheet
) )
514 pDoc
->MakeTable( nTempSheet
);
518 ScCompiler
aCompiler(pDoc
,aAdr
);
519 aCompiler
.SetGrammar(pDoc
->GetGrammar());
520 //if (!ScCompiler::IsInitialized())
521 // ScCompiler::InitSymbolsEnglish();
527 ScTokenArray aTokenArr
;
528 if ( !lcl_AddFunctionToken( aTokenArr
, aName
,aCompiler
) )
530 // function not found
531 throw container::NoSuchElementException();
535 // set options (null date, etc.)
539 pDoc
->SetDocOptions( *pOptions
);
542 // add arguments to token array
545 BOOL bArgErr
= FALSE
;
546 BOOL bOverflow
= FALSE
;
548 long nArgCount
= aArguments
.getLength();
549 const uno::Any
* pArgArr
= aArguments
.getConstArray();
551 aTokenArr
.AddOpCode(ocOpen
);
552 for (long nPos
=0; nPos
<nArgCount
; nPos
++)
555 aTokenArr
.AddOpCode(ocSep
);
557 const uno::Any
& rArg
= pArgArr
[nPos
];
559 uno::TypeClass eClass
= rArg
.getValueTypeClass();
560 uno::Type aType
= rArg
.getValueType();
561 if ( eClass
== uno::TypeClass_BYTE
||
562 eClass
== uno::TypeClass_SHORT
||
563 eClass
== uno::TypeClass_UNSIGNED_SHORT
||
564 eClass
== uno::TypeClass_LONG
||
565 eClass
== uno::TypeClass_UNSIGNED_LONG
||
566 eClass
== uno::TypeClass_FLOAT
||
567 eClass
== uno::TypeClass_DOUBLE
)
569 // #87871# accept integer types because Basic passes a floating point
570 // variable as byte, short or long if it's an integer number.
573 aTokenArr
.AddDouble( fVal
);
575 else if ( eClass
== uno::TypeClass_STRING
)
579 String
aStr( aUStr
);
580 aTokenArr
.AddString( aStr
.GetBuffer() );
582 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<sal_Int16
> > *)0 ) ) )
584 ArrayOfArrayProc
<sal_Int16
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
586 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<sal_Int32
> > *)0 ) ) )
588 ArrayOfArrayProc
<sal_Int32
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
590 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<double> > *)0 ) ) )
592 ArrayOfArrayProc
<double>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
594 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<rtl::OUString
> > *)0 ) ) )
596 ArrayOfArrayProc
<rtl::OUString
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
598 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<uno::Any
> > *)0 ) ) )
600 ArrayOfArrayProc
<uno::Any
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
602 else if ( aType
.equals( getCppuType( (uno::Reference
<table::XCellRange
>*)0 ) ) )
604 // currently, only our own cell ranges are supported
606 uno::Reference
<table::XCellRange
> xRange(rArg
, uno::UNO_QUERY
);
607 ScCellRangesBase
* pImpl
= ScCellRangesBase::getImplementation( xRange
);
610 ScDocument
* pSrcDoc
= pImpl
->GetDocument();
611 const ScRangeList
& rRanges
= pImpl
->GetRangeList();
612 if ( pSrcDoc
&& rRanges
.Count() == 1 )
614 ScRange aSrcRange
= *rRanges
.GetObject(0);
616 long nStartRow
= nDocRow
;
617 long nColCount
= aSrcRange
.aEnd
.Col() - aSrcRange
.aStart
.Col() + 1;
618 long nRowCount
= aSrcRange
.aEnd
.Row() - aSrcRange
.aStart
.Row() + 1;
620 if ( nStartRow
+ nRowCount
> MAXROWCOUNT
)
625 if ( !lcl_CopyData( pSrcDoc
, aSrcRange
, pDoc
, ScAddress( 0, (SCROW
)nDocRow
, 0 ) ) )
629 nDocRow
+= nRowCount
;
631 lcl_AddRef( aTokenArr
, nStartRow
, nColCount
, nRowCount
);
640 bArgErr
= TRUE
; // invalid type
642 aTokenArr
.AddOpCode(ocClose
);
643 aTokenArr
.AddOpCode(ocStop
);
650 if ( !bArgErr
&& !bOverflow
&& nDocRow
<= MAXROWCOUNT
)
652 ScAddress
aFormulaPos( 0, 0, nTempSheet
);
653 // GRAM_PODF_A1 doesn't really matter for the token array but fits with
654 // other API compatibility grammars.
655 ScFormulaCell
* pFormula
= new ScFormulaCell( pDoc
, aFormulaPos
,
656 &aTokenArr
,formula::FormulaGrammar::GRAM_PODF_A1
, MM_FORMULA
);
657 pDoc
->PutCell( aFormulaPos
, pFormula
); //! necessary?
659 // call GetMatrix before GetErrCode because GetMatrix always recalculates
660 // if there is no matrix result
662 const ScMatrix
* pMat
= pFormula
->GetMatrix();
663 USHORT nErrCode
= pFormula
->GetErrCode();
669 ScRangeToSequence::FillMixedArray( aRet
, pMat
);
671 else if ( pFormula
->IsValue() )
674 aRet
<<= (double) pFormula
->GetValue();
680 pFormula
->GetString( aStrVal
);
681 aRet
<<= rtl::OUString( aStrVal
);
684 else if ( nErrCode
== NOTAVAILABLE
)
686 // #N/A: leave result empty, no exception
690 // any other error: IllegalArgumentException
694 pDoc
->DeleteAreaTab( 0, 0, MAXCOL
, MAXROW
, 0, IDF_ALL
);
695 pDoc
->DeleteAreaTab( 0, 0, 0, 0, nTempSheet
, IDF_ALL
);
699 throw uno::RuntimeException();
702 throw lang::IllegalArgumentException();