1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <sfx2/app.hxx>
21 #include <svl/itemprop.hxx>
23 #include "scitems.hxx"
24 #include "funcuno.hxx"
25 #include "miscuno.hxx"
26 #include "cellsuno.hxx"
28 #include "document.hxx"
29 #include "compiler.hxx"
30 #include "formula/errorcodes.hxx"
31 #include "callform.hxx"
32 #include "addincol.hxx"
33 #include "rangeseq.hxx"
34 #include "formulacell.hxx"
35 #include "docoptio.hxx"
39 #include "markdata.hxx"
40 #include "patattr.hxx"
41 #include "docpool.hxx"
43 #include "clipparam.hxx"
44 #include "dociter.hxx"
45 #include "stringutil.hxx"
46 #include "tokenarray.hxx"
48 using namespace com::sun::star
;
50 //------------------------------------------------------------------------
52 // registered as implementation for service FunctionAccess,
53 // also supports service SpreadsheetDocumentSettings (to set null date etc.)
55 #define SCFUNCTIONACCESS_SERVICE "com.sun.star.sheet.FunctionAccess"
56 #define SCDOCSETTINGS_SERVICE "com.sun.star.sheet.SpreadsheetDocumentSettings"
58 //------------------------------------------------------------------------
60 // helper to use cached document if not in use, temporary document otherwise
65 ScTempDocCache
& rCache
;
68 static ScDocument
* CreateDocument(); // create and initialize doc
71 ScTempDocSource( ScTempDocCache
& rDocCache
);
74 ScDocument
* GetDocument();
77 //------------------------------------------------------------------------
79 ScDocument
* ScTempDocSource::CreateDocument()
81 ScDocument
* pDoc
= new ScDocument
; // SCDOCMODE_DOCUMENT
86 ScTempDocSource::ScTempDocSource( ScTempDocCache
& rDocCache
) :
90 if ( rCache
.IsInUse() )
91 pTempDoc
= CreateDocument();
94 rCache
.SetInUse( sal_True
);
95 if ( !rCache
.GetDocument() )
96 rCache
.SetDocument( CreateDocument() );
100 ScTempDocSource::~ScTempDocSource()
105 rCache
.SetInUse( false );
108 ScDocument
* ScTempDocSource::GetDocument()
113 return rCache
.GetDocument();
116 //------------------------------------------------------------------------
118 ScTempDocCache::ScTempDocCache() :
124 ScTempDocCache::~ScTempDocCache()
126 OSL_ENSURE( !bInUse
, "ScTempDocCache dtor: bInUse" );
130 void ScTempDocCache::SetDocument( ScDocument
* pNew
)
132 OSL_ENSURE( !pDoc
, "ScTempDocCache::SetDocument: already set" );
136 void ScTempDocCache::Clear()
138 OSL_ENSURE( !bInUse
, "ScTempDocCache::Clear: bInUse" );
143 //------------------------------------------------------------------------
145 // copy results from one document into another
146 //! merge this with ScAreaLink::Refresh
147 //! copy directly without a clipboard document?
149 static sal_Bool
lcl_CopyData( ScDocument
* pSrcDoc
, const ScRange
& rSrcRange
,
150 ScDocument
* pDestDoc
, const ScAddress
& rDestPos
)
152 SCTAB nSrcTab
= rSrcRange
.aStart
.Tab();
153 SCTAB nDestTab
= rDestPos
.Tab();
155 ScRange
aNewRange( rDestPos
, ScAddress(
156 rSrcRange
.aEnd
.Col() - rSrcRange
.aStart
.Col() + rDestPos
.Col(),
157 rSrcRange
.aEnd
.Row() - rSrcRange
.aStart
.Row() + rDestPos
.Row(),
160 ScDocument
* pClipDoc
= new ScDocument( SCDOCMODE_CLIP
);
161 ScMarkData aSourceMark
;
162 aSourceMark
.SelectOneTable( nSrcTab
); // for CopyToClip
163 aSourceMark
.SetMarkArea( rSrcRange
);
164 ScClipParam
aClipParam(rSrcRange
, false);
165 pSrcDoc
->CopyToClip(aClipParam
, pClipDoc
, &aSourceMark
, false);
167 if ( pClipDoc
->HasAttrib( 0,0,nSrcTab
, MAXCOL
,MAXROW
,nSrcTab
,
168 HASATTR_MERGED
| HASATTR_OVERLAPPED
) )
170 ScPatternAttr
aPattern( pSrcDoc
->GetPool() );
171 aPattern
.GetItemSet().Put( ScMergeAttr() ); // Defaults
172 aPattern
.GetItemSet().Put( ScMergeFlagAttr() );
173 pClipDoc
->ApplyPatternAreaTab( 0,0, MAXCOL
,MAXROW
, nSrcTab
, aPattern
);
176 ScMarkData aDestMark
;
177 aDestMark
.SelectOneTable( nDestTab
);
178 aDestMark
.SetMarkArea( aNewRange
);
179 pDestDoc
->CopyFromClip( aNewRange
, aDestMark
, IDF_ALL
& ~IDF_FORMULA
, NULL
, pClipDoc
, false );
185 //------------------------------------------------------------------------
187 ScFunctionAccess::ScFunctionAccess() :
189 aPropertyMap( ScDocOptionsHelper::GetPropertyMap() ),
190 mbArray( true ), // default according to behaviour of older Office versions
193 StartListening( *SFX_APP() ); // for SFX_HINT_DEINITIALIZING
196 ScFunctionAccess::~ScFunctionAccess()
201 void ScFunctionAccess::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
203 if ( rHint
.ISA(SfxSimpleHint
) &&
204 ((SfxSimpleHint
&)rHint
).GetId() == SFX_HINT_DEINITIALIZING
)
206 // document must not be used anymore
212 // stuff for exService_...
214 uno::Reference
<uno::XInterface
> SAL_CALL
ScFunctionAccess_CreateInstance(
215 const uno::Reference
<lang::XMultiServiceFactory
>& )
217 SolarMutexGuard aGuard
;
219 static uno::Reference
< uno::XInterface
> xInst((::cppu::OWeakObject
*) new ScFunctionAccess
);
223 OUString
ScFunctionAccess::getImplementationName_Static()
225 return OUString( "stardiv.StarCalc.ScFunctionAccess" );
228 uno::Sequence
<OUString
> ScFunctionAccess::getSupportedServiceNames_Static()
230 uno::Sequence
<OUString
> aRet(1);
231 OUString
* pArray
= aRet
.getArray();
232 pArray
[0] = OUString( SCFUNCTIONACCESS_SERVICE
);
238 OUString SAL_CALL
ScFunctionAccess::getImplementationName() throw(uno::RuntimeException
)
240 return OUString( "ScFunctionAccess");
243 sal_Bool SAL_CALL
ScFunctionAccess::supportsService( const OUString
& rServiceName
)
244 throw(uno::RuntimeException
)
246 return rServiceName
.equalsAscii( SCFUNCTIONACCESS_SERVICE
) ||
247 rServiceName
.equalsAscii( SCDOCSETTINGS_SERVICE
);
250 uno::Sequence
<OUString
> SAL_CALL
ScFunctionAccess::getSupportedServiceNames()
251 throw(uno::RuntimeException
)
253 uno::Sequence
<OUString
> aRet(2);
254 OUString
* pArray
= aRet
.getArray();
255 pArray
[0] = OUString( SCFUNCTIONACCESS_SERVICE
);
256 pArray
[1] = OUString( SCDOCSETTINGS_SERVICE
);
260 // XPropertySet (document settings)
262 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
ScFunctionAccess::getPropertySetInfo()
263 throw(uno::RuntimeException
)
265 SolarMutexGuard aGuard
;
266 static uno::Reference
<beans::XPropertySetInfo
> aRef(
267 new SfxItemPropertySetInfo( aPropertyMap
));
271 void SAL_CALL
ScFunctionAccess::setPropertyValue(
272 const OUString
& aPropertyName
, const uno::Any
& aValue
)
273 throw(beans::UnknownPropertyException
, beans::PropertyVetoException
,
274 lang::IllegalArgumentException
, lang::WrappedTargetException
,
275 uno::RuntimeException
)
277 SolarMutexGuard aGuard
;
279 if ( aPropertyName
== "IsArrayFunction" )
281 if( !(aValue
>>= mbArray
) )
282 throw lang::IllegalArgumentException();
287 pOptions
= new ScDocOptions();
289 // options aren't initialized from configuration - always get the same default behaviour
291 sal_Bool bDone
= ScDocOptionsHelper::setPropertyValue( *pOptions
, aPropertyMap
, aPropertyName
, aValue
);
293 throw beans::UnknownPropertyException();
297 uno::Any SAL_CALL
ScFunctionAccess::getPropertyValue( const OUString
& aPropertyName
)
298 throw(beans::UnknownPropertyException
, lang::WrappedTargetException
,
299 uno::RuntimeException
)
301 SolarMutexGuard aGuard
;
303 if ( aPropertyName
== "IsArrayFunction" )
304 return uno::Any( mbArray
);
307 pOptions
= new ScDocOptions();
309 // options aren't initialized from configuration - always get the same default behaviour
311 return ScDocOptionsHelper::getPropertyValue( *pOptions
, aPropertyMap
, aPropertyName
);
314 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScFunctionAccess
)
318 static sal_Bool
lcl_AddFunctionToken( ScTokenArray
& rArray
, const OUString
& rName
,const ScCompiler
& rCompiler
)
320 // function names are always case-insensitive
321 OUString aUpper
= ScGlobal::pCharClass
->uppercase(rName
);
323 // same options as in ScCompiler::IsOpCode:
324 // 1. built-in function name
326 OpCode eOp
= rCompiler
.GetEnglishOpCode( aUpper
);
329 rArray
.AddOpCode( eOp
);
333 // 2. old add in functions
335 if (ScGlobal::GetFuncCollection()->findByName(aUpper
))
337 rArray
.AddExternal(aUpper
.getStr());
341 // 3. new (uno) add in functions
344 ScGlobal::GetAddInCollection()->FindFunction(aUpper
, false);
345 if (!aIntName
.isEmpty())
347 rArray
.AddExternal(aIntName
.getStr()); // international name
351 return false; // no valid function name
354 static void lcl_AddRef( ScTokenArray
& rArray
, long nStartRow
, long nColCount
, long nRowCount
)
356 ScComplexRefData aRef
;
357 aRef
.InitRange(ScRange(0,nStartRow
,0,nColCount
-1,nStartRow
+nRowCount
-1,0));
358 rArray
.AddDoubleReference(aRef
);
367 SimpleVisitor( ScDocument
* pDoc
) : mbArgError( false ), mpDoc( pDoc
) {}
368 // could possibly just get away with JUST the following overload
369 // 1) virtual void visitElem( long& nCol, long& nRow, const double& elem )
370 // 2) virtual void visitElem( long& nCol, long& nRow, const OUString& elem )
371 // 3) virtual void visitElem( long& nCol, long& nRow, const uno::Any& elem )
372 // the other types methods are here just to reflect the orig code and for
375 void visitElem( long nCol
, long nRow
, const sal_Int16
& elem
)
377 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
379 void visitElem( long nCol
, long nRow
, const sal_Int32
& elem
)
381 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
383 void visitElem( long nCol
, long nRow
, const double& elem
)
385 mpDoc
->SetValue( (SCCOL
) nCol
, (SCROW
) nRow
, 0, elem
);
387 void visitElem( long nCol
, long nRow
, const OUString
& elem
)
391 ScSetStringParam aParam
;
392 aParam
.setTextInput();
393 mpDoc
->SetString(ScAddress(nCol
,nRow
,0), elem
, &aParam
);
396 void visitElem( long nCol
, long nRow
, const uno::Any
& rElement
)
398 uno::TypeClass eElemClass
= rElement
.getValueTypeClass();
399 if ( eElemClass
== uno::TypeClass_VOID
)
403 else if ( eElemClass
== uno::TypeClass_BYTE
||
404 eElemClass
== uno::TypeClass_SHORT
||
405 eElemClass
== uno::TypeClass_UNSIGNED_SHORT
||
406 eElemClass
== uno::TypeClass_LONG
||
407 eElemClass
== uno::TypeClass_UNSIGNED_LONG
||
408 eElemClass
== uno::TypeClass_FLOAT
||
409 eElemClass
== uno::TypeClass_DOUBLE
)
411 // accept integer types because Basic passes a floating point
412 // variable as byte, short or long if it's an integer number.
415 visitElem( nCol
, nRow
, fVal
);
417 else if ( eElemClass
== uno::TypeClass_STRING
)
421 visitElem( nCol
, nRow
, aUStr
);
426 bool hasArgError() const { return mbArgError
; }
429 template< class seq
>
430 class SequencesContainer
432 uno::Sequence
< uno::Sequence
< seq
> > maSeq
;
438 ScTokenArray
& mrTokenArr
;
441 SequencesContainer( const uno::Any
& rArg
, ScTokenArray
& rTokenArr
, long& rDocRow
, ScDocument
* pDoc
) :
442 mrDocRow( rDocRow
), mbOverflow(false), mbArgError(false), mpDoc( pDoc
), mrTokenArr( rTokenArr
)
449 SimpleVisitor
aVisitor(mpDoc
);
450 long nStartRow
= mrDocRow
;
451 long nRowCount
= maSeq
.getLength();
452 long nMaxColCount
= 0;
453 const uno::Sequence
< seq
>* pRowArr
= maSeq
.getConstArray();
454 for ( long nRow
=0; nRow
<nRowCount
; nRow
++ )
456 long nColCount
= pRowArr
[nRow
].getLength();
457 if ( nColCount
> nMaxColCount
)
458 nMaxColCount
= nColCount
;
459 const seq
* pColArr
= pRowArr
[nRow
].getConstArray();
460 for (long nCol
=0; nCol
<nColCount
; nCol
++)
461 if ( nCol
<= MAXCOL
&& mrDocRow
<= MAXROW
)
462 aVisitor
.visitElem( nCol
, mrDocRow
, pColArr
[ nCol
] );
467 mbArgError
= aVisitor
.hasArgError();
468 if ( nRowCount
&& nMaxColCount
&& !mbOverflow
)
469 lcl_AddRef( mrTokenArr
, nStartRow
, nMaxColCount
, nRowCount
);
471 bool getOverflow() const { return mbOverflow
; }
472 bool getArgError() const { return mbArgError
; }
476 class ArrayOfArrayProc
479 static void processSequences( ScDocument
* pDoc
, const uno::Any
& rArg
, ScTokenArray
& rTokenArr
,
480 long& rDocRow
, sal_Bool
& rArgErr
, sal_Bool
& rOverflow
)
482 SequencesContainer
< T
> aContainer( rArg
, rTokenArr
, rDocRow
, pDoc
);
483 aContainer
.process();
484 rArgErr
= aContainer
.getArgError();
485 rOverflow
= aContainer
.getOverflow();
489 uno::Any SAL_CALL
ScFunctionAccess::callFunction( const OUString
& aName
,
490 const uno::Sequence
<uno::Any
>& aArguments
)
491 throw(container::NoSuchElementException
, lang::IllegalArgumentException
,
492 uno::RuntimeException
)
494 SolarMutexGuard aGuard
;
497 throw uno::RuntimeException();
499 // use cached document if not in use, temporary document otherwise
500 // (deleted in ScTempDocSource dtor)
501 ScTempDocSource
aSource( aDocCache
);
502 ScDocument
* pDoc
= aSource
.GetDocument();
503 const static SCTAB nTempSheet
= 1;
504 // Create an extra tab to contain the Function Cell
505 // this will allow full rows to be used.
506 if ( !pDoc
->HasTable( nTempSheet
) )
507 pDoc
->MakeTable( nTempSheet
);
511 ScCompiler
aCompiler(pDoc
,aAdr
);
512 aCompiler
.SetGrammar(pDoc
->GetGrammar());
518 ScTokenArray aTokenArr
;
519 if ( !lcl_AddFunctionToken( aTokenArr
, aName
,aCompiler
) )
521 // function not found
522 throw container::NoSuchElementException();
526 // set options (null date, etc.)
530 pDoc
->SetDocOptions( *pOptions
);
533 // add arguments to token array
536 sal_Bool bArgErr
= false;
537 sal_Bool bOverflow
= false;
539 long nArgCount
= aArguments
.getLength();
540 const uno::Any
* pArgArr
= aArguments
.getConstArray();
542 aTokenArr
.AddOpCode(ocOpen
);
543 for (long nPos
=0; nPos
<nArgCount
; nPos
++)
546 aTokenArr
.AddOpCode(ocSep
);
548 const uno::Any
& rArg
= pArgArr
[nPos
];
550 uno::TypeClass eClass
= rArg
.getValueTypeClass();
551 uno::Type aType
= rArg
.getValueType();
552 if ( eClass
== uno::TypeClass_BYTE
||
553 eClass
== uno::TypeClass_BOOLEAN
||
554 eClass
== uno::TypeClass_SHORT
||
555 eClass
== uno::TypeClass_UNSIGNED_SHORT
||
556 eClass
== uno::TypeClass_LONG
||
557 eClass
== uno::TypeClass_UNSIGNED_LONG
||
558 eClass
== uno::TypeClass_FLOAT
||
559 eClass
== uno::TypeClass_DOUBLE
)
561 // accept integer types because Basic passes a floating point
562 // variable as byte, short or long if it's an integer number.
565 aTokenArr
.AddDouble( fVal
);
567 else if ( eClass
== uno::TypeClass_STRING
)
571 aTokenArr
.AddString( aUStr
);
573 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<sal_Int16
> > *)0 ) ) )
575 ArrayOfArrayProc
<sal_Int16
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
577 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<sal_Int32
> > *)0 ) ) )
579 ArrayOfArrayProc
<sal_Int32
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
581 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<double> > *)0 ) ) )
583 ArrayOfArrayProc
<double>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
585 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<OUString
> > *)0 ) ) )
587 ArrayOfArrayProc
<OUString
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
589 else if ( aType
.equals( getCppuType( (uno::Sequence
< uno::Sequence
<uno::Any
> > *)0 ) ) )
591 ArrayOfArrayProc
<uno::Any
>::processSequences( pDoc
, rArg
, aTokenArr
, nDocRow
, bArgErr
, bOverflow
);
593 else if ( aType
.equals( getCppuType( (uno::Reference
<table::XCellRange
>*)0 ) ) )
595 // currently, only our own cell ranges are supported
597 uno::Reference
<table::XCellRange
> xRange(rArg
, uno::UNO_QUERY
);
598 ScCellRangesBase
* pImpl
= ScCellRangesBase::getImplementation( xRange
);
601 ScDocument
* pSrcDoc
= pImpl
->GetDocument();
602 const ScRangeList
& rRanges
= pImpl
->GetRangeList();
603 if ( pSrcDoc
&& rRanges
.size() == 1 )
605 ScRange aSrcRange
= *rRanges
[ 0 ];
607 long nStartRow
= nDocRow
;
608 long nColCount
= aSrcRange
.aEnd
.Col() - aSrcRange
.aStart
.Col() + 1;
609 long nRowCount
= aSrcRange
.aEnd
.Row() - aSrcRange
.aStart
.Row() + 1;
611 if ( nStartRow
+ nRowCount
> MAXROWCOUNT
)
612 bOverflow
= sal_True
;
616 if ( !lcl_CopyData( pSrcDoc
, aSrcRange
, pDoc
, ScAddress( 0, (SCROW
)nDocRow
, 0 ) ) )
617 bOverflow
= sal_True
;
620 nDocRow
+= nRowCount
;
622 lcl_AddRef( aTokenArr
, nStartRow
, nColCount
, nRowCount
);
631 bArgErr
= sal_True
; // invalid type
633 aTokenArr
.AddOpCode(ocClose
);
634 aTokenArr
.AddOpCode(ocStop
);
641 if ( !bArgErr
&& !bOverflow
&& nDocRow
<= MAXROWCOUNT
)
643 ScAddress
aFormulaPos( 0, 0, nTempSheet
);
644 // GRAM_PODF_A1 doesn't really matter for the token array but fits with
645 // other API compatibility grammars.
646 ScFormulaCell
* pFormula
= new ScFormulaCell(
647 pDoc
, aFormulaPos
, aTokenArr
, formula::FormulaGrammar::GRAM_PODF_A1
,
648 (sal_uInt8
)(mbArray
? MM_FORMULA
: MM_NONE
) );
649 pFormula
= pDoc
->SetFormulaCell(aFormulaPos
, pFormula
);
651 // call GetMatrix before GetErrCode because GetMatrix always recalculates
652 // if there is no matrix result
654 const ScMatrix
* pMat
= (mbArray
&& pFormula
) ? pFormula
->GetMatrix() : 0;
655 sal_uInt16 nErrCode
= pFormula
? pFormula
->GetErrCode() : errIllegalArgument
;
661 ScRangeToSequence::FillMixedArray( aRet
, pMat
);
663 else if ( pFormula
->IsValue() )
666 aRet
<<= (double) pFormula
->GetValue();
671 OUString aStrVal
= pFormula
->GetString().getString();
675 else if ( nErrCode
== NOTAVAILABLE
)
677 // #N/A: leave result empty, no exception
681 // any other error: IllegalArgumentException
685 pDoc
->DeleteAreaTab( 0, 0, MAXCOL
, MAXROW
, 0, IDF_ALL
);
686 pDoc
->DeleteAreaTab( 0, 0, 0, 0, nTempSheet
, IDF_ALL
);
690 throw uno::RuntimeException();
693 throw lang::IllegalArgumentException();
699 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */