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 .
19 #include <com/sun/star/beans/XPropertySet.hpp>
20 #include <com/sun/star/table/XCellRange.hpp>
21 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
22 #include <com/sun/star/sheet/XFunctionAccess.hpp>
23 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
24 #include <ooo/vba/excel/XRange.hpp>
26 #include "vbawsfunction.hxx"
27 #include <compiler.hxx>
29 using namespace com::sun::star
;
30 using namespace ooo::vba
;
34 void lclConvertDoubleToBoolean( uno::Any
& rAny
)
36 if( rAny
.has
< double >() )
38 double fValue
= rAny
.get
< double >();
41 else if( fValue
== 1.0 )
43 // do nothing for other values or types
47 void lclConvertBooleanToDouble( uno::Any
& rAny
)
50 if ( rAny
>>= bValue
)
61 ScVbaWSFunction::ScVbaWSFunction( const uno::Reference
< XHelperInterface
>& xParent
, const css::uno::Reference
< css::uno::XComponentContext
>& xContext
) :
62 ScVbaWSFunction_BASE( xParent
, xContext
)
66 uno::Reference
< beans::XIntrospectionAccess
>
67 ScVbaWSFunction::getIntrospection()
69 return uno::Reference
<beans::XIntrospectionAccess
>();
73 ScVbaWSFunction::invoke(const OUString
& FunctionName
, const uno::Sequence
< uno::Any
>& Params
, uno::Sequence
< sal_Int16
>& /*OutParamIndex*/, uno::Sequence
< uno::Any
>& /*OutParam*/)
75 // create copy of parameters, replace Excel range objects with UNO range objects
76 uno::Sequence
< uno::Any
> aParamTemp( Params
);
77 if( aParamTemp
.hasElements() )
79 for( uno::Any
& rArray
: asNonConstRange(aParamTemp
) )
81 switch( rArray
.getValueTypeClass() )
83 case uno::TypeClass_BOOLEAN
:
84 lclConvertBooleanToDouble( rArray
);
86 case uno::TypeClass_INTERFACE
:
88 uno::Reference
< excel::XRange
> myRange( rArray
, uno::UNO_QUERY
);
90 rArray
= myRange
->getCellRange();
93 case uno::TypeClass_SEQUENCE
:
95 // the sheet.FunctionAccess service doesn't deal with Sequences, only Sequences of Sequence
96 uno::Type aType
= rArray
.getValueType();
97 if ( aType
.equals( cppu::UnoType
<uno::Sequence
<sal_Int16
>>::get() ) )
99 uno::Sequence
< uno::Sequence
< sal_Int16
> > aTmp(1);
100 rArray
>>= aTmp
.getArray()[ 0 ];
103 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<sal_Int32
>>::get() ) )
105 uno::Sequence
< uno::Sequence
< sal_Int32
> > aTmp(1);
106 rArray
>>= aTmp
.getArray()[ 0 ];
109 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<double>>::get() ) )
111 uno::Sequence
< uno::Sequence
< double > > aTmp(1);
112 rArray
>>= aTmp
.getArray()[ 0 ];
115 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<OUString
>>::get() ) )
117 uno::Sequence
< uno::Sequence
< OUString
> > aTmp(1);
118 rArray
>>= aTmp
.getArray()[ 0 ];
121 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<uno::Any
>>::get() ) )
123 uno::Sequence
< uno::Sequence
<uno::Any
> > aTmp(1);
124 rArray
>>= aTmp
.getArray()[ 0 ];
136 bool bAsArray
= true;
138 // special handing for some functions that don't work correctly in FunctionAccess
139 formula::FormulaCompiler aCompiler
;
140 OpCode eOpCode
= aCompiler
.GetEnglishOpCode( FunctionName
.toAsciiUpperCase() );
143 // ISLOGICAL does not work in array formula mode
146 if( aParamTemp
.getLength() != 1 )
147 throw lang::IllegalArgumentException();
148 const uno::Any
& rParam
= aParamTemp
[ 0 ];
149 if( rParam
.has
< bool >() )
153 else if( rParam
.has
< uno::Reference
< table::XCellRange
> >() ) try
155 uno::Reference
< sheet::XCellRangeAddressable
> xRangeAddr( rParam
, uno::UNO_QUERY_THROW
);
156 table::CellRangeAddress aRangeAddr
= xRangeAddr
->getRangeAddress();
157 bAsArray
= (aRangeAddr
.StartColumn
!= aRangeAddr
.EndColumn
) || (aRangeAddr
.StartRow
!= aRangeAddr
.EndRow
);
159 catch( uno::Exception
& )
167 if( !aRet
.hasValue() )
169 uno::Reference
< lang::XMultiComponentFactory
> xSMgr( mxContext
->getServiceManager(), uno::UNO_SET_THROW
);
170 uno::Reference
< sheet::XFunctionAccess
> xFunctionAccess( xSMgr
->createInstanceWithContext(
171 u
"com.sun.star.sheet.FunctionAccess"_ustr
, mxContext
),
172 uno::UNO_QUERY_THROW
);
173 uno::Reference
< beans::XPropertySet
> xPropSet( xFunctionAccess
, uno::UNO_QUERY_THROW
);
174 xPropSet
->setPropertyValue(u
"IsArrayFunction"_ustr
, uno::Any( bAsArray
) );
175 aRet
= xFunctionAccess
->callFunction( FunctionName
, aParamTemp
);
178 /* Convert return value from double to Boolean for some functions that
180 typedef uno::Sequence
< uno::Sequence
< uno::Any
> > AnySeqSeq
;
181 if( (eOpCode
== ocIsEmpty
) || (eOpCode
== ocIsString
) || (eOpCode
== ocIsNonString
) || (eOpCode
== ocIsLogical
) ||
182 (eOpCode
== ocIsRef
) || (eOpCode
== ocIsValue
) || (eOpCode
== ocIsFormula
) || (eOpCode
== ocIsNA
) ||
183 (eOpCode
== ocIsErr
) || (eOpCode
== ocIsError
) || (eOpCode
== ocIsEven
) || (eOpCode
== ocIsOdd
) ||
184 (eOpCode
== ocAnd
) || (eOpCode
== ocOr
) || (eOpCode
== ocXor
) || (eOpCode
== ocNot
) || (eOpCode
== ocTrue
) || (eOpCode
== ocFalse
) )
186 if( aRet
.has
< AnySeqSeq
>() )
188 AnySeqSeq aAnySeqSeq
= aRet
.get
< AnySeqSeq
>();
189 for( auto& rAnySeq
: asNonConstRange(aAnySeqSeq
) )
190 for( auto& rAny
: asNonConstRange(rAnySeq
) )
191 lclConvertDoubleToBoolean( rAny
);
196 lclConvertDoubleToBoolean( aRet
);
200 /* Hack/workaround (?): shorten single-row matrix to simple array, shorten
201 1x1 matrix to single value. */
202 if( aRet
.has
< AnySeqSeq
>() )
204 AnySeqSeq aAnySeqSeq
= aRet
.get
< AnySeqSeq
>();
205 if( aAnySeqSeq
.getLength() == 1 )
207 if( aAnySeqSeq
[ 0 ].getLength() == 1 )
208 aRet
= aAnySeqSeq
[ 0 ][ 0 ];
210 aRet
<<= aAnySeqSeq
[ 0 ];
215 // MATCH function should alwayse return a double value, but currently if the first argument is XCellRange, MATCH function returns an array instead of a double value. Don't know why?
216 // To fix this issue in safe, current solution is to convert this array to a double value just for MATCH function.
217 OUString
aUpper( FunctionName
.toAsciiUpperCase() );
218 ScCompiler
aCompiler( NULL
, ScAddress() );
219 OpCode eOp
= aCompiler
.GetEnglishOpCode( aUpper
);
225 uno::Sequence
< uno::Sequence
< uno::Any
> > aSequence
;
226 if( !( ( aRet
>>= aSequence
) && ( aSequence
.getLength() > 0 ) &&
227 ( aSequence
[0].getLength() > 0 ) && ( aSequence
[0][0] >>= fVal
) ) )
228 throw uno::RuntimeException();
237 ScVbaWSFunction::setValue(const OUString
& /*PropertyName*/, const uno::Any
& /*Value*/)
239 throw beans::UnknownPropertyException();
243 ScVbaWSFunction::getValue(const OUString
& /*PropertyName*/)
245 throw beans::UnknownPropertyException();
249 ScVbaWSFunction::hasMethod(const OUString
& Name
)
251 bool bIsFound
= false;
254 // the function name contained in the com.sun.star.sheet.FunctionDescription service is alwayse localized.
255 // but the function name used in WorksheetFunction is a programmatic name (seems English).
256 // So m_xNameAccess->hasByName( Name ) may fail to find name when a function name has a localized name.
257 if( ScCompiler::IsEnglishSymbol( Name
) )
260 catch( uno::Exception
& /*e*/ )
262 // failed to find name
268 ScVbaWSFunction::hasProperty(const OUString
& /*Name*/)
274 ScVbaWSFunction::getExactName( const OUString
& aApproximateName
)
276 OUString sName
= aApproximateName
.toAsciiUpperCase();
277 if ( !hasMethod( sName
) )
283 ScVbaWSFunction::getServiceImplName()
285 return u
"ScVbaWSFunction"_ustr
;
288 uno::Sequence
< OUString
>
289 ScVbaWSFunction::getServiceNames()
291 static uno::Sequence
< OUString
> const aServiceNames
293 u
"ooo.vba.excel.WorksheetFunction"_ustr
295 return aServiceNames
;
298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */