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/XCell.hpp>
21 #include <com/sun/star/table/XColumnRowRange.hpp>
22 #include <com/sun/star/beans/XIntrospection.hpp>
23 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
24 #include <com/sun/star/sheet/XFunctionAccess.hpp>
25 #include <com/sun/star/sheet/XCellRangesQuery.hpp>
26 #include <com/sun/star/sheet/XCellRangeAddressable.hpp>
27 #include <com/sun/star/sheet/CellFlags.hpp>
28 #include <com/sun/star/reflection/XIdlMethod.hpp>
29 #include <com/sun/star/beans/MethodConcept.hpp>
30 #include <comphelper/processfactory.hxx>
31 #include <cppuhelper/queryinterface.hxx>
32 #include <comphelper/anytostring.hxx>
34 #include "vbawsfunction.hxx"
35 #include "compiler.hxx"
37 using namespace com::sun::star
;
38 using namespace ooo::vba
;
42 void lclConvertDoubleToBoolean( uno::Any
& rAny
)
44 if( rAny
.has
< double >() )
46 double fValue
= rAny
.get
< double >();
49 else if( fValue
== 1.0 )
51 // do nothing for other values or types
55 void lclConvertBooleanToDouble( uno::Any
& rAny
)
58 if ( rAny
>>= bValue
)
61 rAny
<<= double( 1.0 );
63 rAny
<<= double( 0.0 );
69 ScVbaWSFunction::ScVbaWSFunction( const uno::Reference
< XHelperInterface
>& xParent
, const css::uno::Reference
< css::uno::XComponentContext
>& xContext
) :
70 ScVbaWSFunction_BASE( xParent
, xContext
)
74 uno::Reference
< beans::XIntrospectionAccess
>
75 ScVbaWSFunction::getIntrospection() throw(uno::RuntimeException
, std::exception
)
77 return uno::Reference
<beans::XIntrospectionAccess
>();
81 ScVbaWSFunction::invoke(const OUString
& FunctionName
, const uno::Sequence
< uno::Any
>& Params
, uno::Sequence
< sal_Int16
>& /*OutParamIndex*/, uno::Sequence
< uno::Any
>& /*OutParam*/) throw(lang::IllegalArgumentException
, script::CannotConvertException
, reflection::InvocationTargetException
, uno::RuntimeException
, std::exception
)
83 // create copy of parameters, replace Excel range objects with UNO range objects
84 uno::Sequence
< uno::Any
> aParamTemp( Params
);
85 if( aParamTemp
.hasElements() )
87 uno::Any
* pArray
= aParamTemp
.getArray();
88 uno::Any
* pArrayEnd
= pArray
+ aParamTemp
.getLength();
89 for( ; pArray
< pArrayEnd
; ++pArray
)
91 switch( pArray
->getValueType().getTypeClass() )
93 case uno::TypeClass_BOOLEAN
:
94 lclConvertBooleanToDouble( *pArray
);
96 case uno::TypeClass_INTERFACE
:
98 uno::Reference
< excel::XRange
> myRange( *pArray
, uno::UNO_QUERY
);
100 *pArray
= myRange
->getCellRange();
103 case uno::TypeClass_SEQUENCE
:
105 // the sheet.FunctionAccess service doesn't deal with Sequences, only Sequences of Sequence
106 uno::Type aType
= pArray
->getValueType();
107 if ( aType
.equals( cppu::UnoType
<uno::Sequence
<sal_Int16
>>::get() ) )
109 uno::Sequence
< uno::Sequence
< sal_Int16
> > aTmp(1);
110 (*pArray
) >>= aTmp
[ 0 ];
113 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<sal_Int32
>>::get() ) )
115 uno::Sequence
< uno::Sequence
< sal_Int32
> > aTmp(1);
116 (*pArray
) >>= aTmp
[ 0 ];
119 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<double>>::get() ) )
121 uno::Sequence
< uno::Sequence
< double > > aTmp(1);
122 (*pArray
) >>= aTmp
[ 0 ];
125 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<OUString
>>::get() ) )
127 uno::Sequence
< uno::Sequence
< OUString
> > aTmp(1);
128 (*pArray
) >>= aTmp
[ 0 ];
131 else if ( aType
.equals( cppu::UnoType
<uno::Sequence
<uno::Any
>>::get() ) )
133 uno::Sequence
< uno::Sequence
<uno::Any
> > aTmp(1);
134 (*pArray
) >>= aTmp
[ 0 ];
142 OSL_TRACE("Param[%d] is %s", (int)(pArray
- aParamTemp
.getConstArray()), OUStringToOString( comphelper::anyToString( *pArray
), RTL_TEXTENCODING_UTF8
).getStr() );
147 bool bAsArray
= true;
149 // special handing for some functions that don't work correctly in FunctionAccess
150 ScCompiler
aCompiler( 0, ScAddress() );
151 OpCode eOpCode
= aCompiler
.GetEnglishOpCode( FunctionName
.toAsciiUpperCase() );
154 // ISLOGICAL does not work in array formula mode
157 if( aParamTemp
.getLength() != 1 )
158 throw lang::IllegalArgumentException();
159 const uno::Any
& rParam
= aParamTemp
[ 0 ];
160 if( rParam
.has
< bool >() )
164 else if( rParam
.has
< uno::Reference
< table::XCellRange
> >() ) try
166 uno::Reference
< sheet::XCellRangeAddressable
> xRangeAddr( rParam
, uno::UNO_QUERY_THROW
);
167 table::CellRangeAddress aRangeAddr
= xRangeAddr
->getRangeAddress();
168 bAsArray
= (aRangeAddr
.StartColumn
!= aRangeAddr
.EndColumn
) || (aRangeAddr
.StartRow
!= aRangeAddr
.EndRow
);
170 catch( uno::Exception
& )
178 if( !aRet
.hasValue() )
180 uno::Reference
< lang::XMultiComponentFactory
> xSMgr( mxContext
->getServiceManager(), uno::UNO_QUERY_THROW
);
181 uno::Reference
< sheet::XFunctionAccess
> xFunctionAccess( xSMgr
->createInstanceWithContext(
182 OUString( "com.sun.star.sheet.FunctionAccess" ), mxContext
),
183 uno::UNO_QUERY_THROW
);
184 uno::Reference
< beans::XPropertySet
> xPropSet( xFunctionAccess
, uno::UNO_QUERY_THROW
);
185 xPropSet
->setPropertyValue("IsArrayFunction", uno::Any( bAsArray
) );
186 aRet
= xFunctionAccess
->callFunction( FunctionName
, aParamTemp
);
189 /* Convert return value from double to Boolean for some functions that
191 typedef uno::Sequence
< uno::Sequence
< uno::Any
> > AnySeqSeq
;
192 if( (eOpCode
== ocIsEmpty
) || (eOpCode
== ocIsString
) || (eOpCode
== ocIsNonString
) || (eOpCode
== ocIsLogical
) ||
193 (eOpCode
== ocIsRef
) || (eOpCode
== ocIsValue
) || (eOpCode
== ocIsFormula
) || (eOpCode
== ocIsNA
) ||
194 (eOpCode
== ocIsErr
) || (eOpCode
== ocIsError
) || (eOpCode
== ocIsEven
) || (eOpCode
== ocIsOdd
) ||
195 (eOpCode
== ocAnd
) || (eOpCode
== ocOr
) || (eOpCode
== ocXor
) || (eOpCode
== ocNot
) || (eOpCode
== ocTrue
) || (eOpCode
== ocFalse
) )
197 if( aRet
.has
< AnySeqSeq
>() )
199 AnySeqSeq aAnySeqSeq
= aRet
.get
< AnySeqSeq
>();
200 for( sal_Int32 nRow
= 0; nRow
< aAnySeqSeq
.getLength(); ++nRow
)
201 for( sal_Int32 nCol
= 0; nCol
< aAnySeqSeq
[ nRow
].getLength(); ++nCol
)
202 lclConvertDoubleToBoolean( aAnySeqSeq
[ nRow
][ nCol
] );
207 lclConvertDoubleToBoolean( aRet
);
211 /* Hack/workaround (?): shorten single-row matrix to simple array, shorten
212 1x1 matrix to single value. */
213 if( aRet
.has
< AnySeqSeq
>() )
215 AnySeqSeq aAnySeqSeq
= aRet
.get
< AnySeqSeq
>();
216 if( aAnySeqSeq
.getLength() == 1 )
218 if( aAnySeqSeq
[ 0 ].getLength() == 1 )
219 aRet
= aAnySeqSeq
[ 0 ][ 0 ];
221 aRet
<<= aAnySeqSeq
[ 0 ];
226 // 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?
227 // To fix this issue in safe, current solution is to convert this array to a double value just for MATCH function.
228 OUString
aUpper( FunctionName
.toAsciiUpperCase() );
229 ScCompiler
aCompiler( NULL
, ScAddress() );
230 OpCode eOp
= aCompiler
.GetEnglishOpCode( aUpper
);
236 uno::Sequence
< uno::Sequence
< uno::Any
> > aSequence
;
237 if( !( ( aRet
>>= aSequence
) && ( aSequence
.getLength() > 0 ) &&
238 ( aSequence
[0].getLength() > 0 ) && ( aSequence
[0][0] >>= fVal
) ) )
239 throw uno::RuntimeException();
248 ScVbaWSFunction::setValue(const OUString
& /*PropertyName*/, const uno::Any
& /*Value*/) throw(beans::UnknownPropertyException
, script::CannotConvertException
, reflection::InvocationTargetException
, uno::RuntimeException
, std::exception
)
250 throw beans::UnknownPropertyException();
254 ScVbaWSFunction::getValue(const OUString
& /*PropertyName*/) throw(beans::UnknownPropertyException
, uno::RuntimeException
, std::exception
)
256 throw beans::UnknownPropertyException();
260 ScVbaWSFunction::hasMethod(const OUString
& Name
) throw(uno::RuntimeException
, std::exception
)
262 bool bIsFound
= false;
265 // the function name contained in the com.sun.star.sheet.FunctionDescription service is alwayse localized.
266 // but the function name used in WorksheetFunction is a programmatic name (seems English).
267 // So m_xNameAccess->hasByName( Name ) may fail to find name when a function name has a localized name.
268 ScCompiler
aCompiler( NULL
, ScAddress() );
269 if( aCompiler
.IsEnglishSymbol( Name
) )
272 catch( uno::Exception
& /*e*/ )
274 // failed to find name
280 ScVbaWSFunction::hasProperty(const OUString
& /*Name*/) throw(uno::RuntimeException
, std::exception
)
286 ScVbaWSFunction::getExactName( const OUString
& aApproximateName
) throw (css::uno::RuntimeException
, std::exception
)
288 OUString sName
= aApproximateName
.toAsciiUpperCase();
289 if ( !hasMethod( sName
) )
295 ScVbaWSFunction::getServiceImplName()
297 return OUString("ScVbaWSFunction");
300 uno::Sequence
< OUString
>
301 ScVbaWSFunction::getServiceNames()
303 static uno::Sequence
< OUString
> aServiceNames
;
304 if ( aServiceNames
.getLength() == 0 )
306 aServiceNames
.realloc( 1 );
307 aServiceNames
[ 0 ] = "ooo.vba.excel.WorksheetFunction";
309 return aServiceNames
;
312 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */