| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/table/XCell.hpp> |
| #include <com/sun/star/table/XColumnRowRange.hpp> |
| #include <com/sun/star/beans/XIntrospection.hpp> |
| #include <com/sun/star/beans/XIntrospectionAccess.hpp> |
| #include <com/sun/star/sheet/XFunctionAccess.hpp> |
| #include <com/sun/star/sheet/XCellRangesQuery.hpp> |
| #include <com/sun/star/sheet/XCellRangeAddressable.hpp> |
| #include <com/sun/star/sheet/CellFlags.hpp> |
| #include <com/sun/star/reflection/XIdlMethod.hpp> |
| #include <com/sun/star/beans/MethodConcept.hpp> |
| #include <comphelper/processfactory.hxx> |
| #include <cppuhelper/queryinterface.hxx> |
| #include <comphelper/anytostring.hxx> |
| |
| #include "vbawsfunction.hxx" |
| #include "compiler.hxx" |
| |
| using namespace com::sun::star; |
| using namespace ooo::vba; |
| |
| namespace { |
| |
| void lclConvertDoubleToBoolean( uno::Any& rAny ) |
| { |
| if( rAny.has< double >() ) |
| { |
| double fValue = rAny.get< double >(); |
| if( fValue == 0.0 ) |
| rAny <<= false; |
| else if( fValue == 1.0 ) |
| rAny <<= true; |
| // do nothing for other values or types |
| } |
| } |
| |
| } // namespace |
| |
| ScVbaWSFunction::ScVbaWSFunction( const uno::Reference< XHelperInterface >& xParent, const css::uno::Reference< css::uno::XComponentContext >& xContext ) : |
| ScVbaWSFunction_BASE( xParent, xContext ) |
| { |
| } |
| |
| uno::Reference< beans::XIntrospectionAccess > |
| ScVbaWSFunction::getIntrospection(void) throw(uno::RuntimeException) |
| { |
| return uno::Reference<beans::XIntrospectionAccess>(); |
| } |
| |
| uno::Any SAL_CALL |
| ScVbaWSFunction::invoke(const rtl::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) |
| { |
| // create copy of parameters, replace Excel range objects with UNO range objects |
| uno::Sequence< uno::Any > aParamTemp( Params ); |
| if( aParamTemp.hasElements() ) |
| { |
| uno::Any* pArray = aParamTemp.getArray(); |
| uno::Any* pArrayEnd = pArray + aParamTemp.getLength(); |
| for( ; pArray < pArrayEnd; ++pArray ) |
| { |
| uno::Reference< excel::XRange > myRange( *pArray, uno::UNO_QUERY ); |
| if( myRange.is() ) |
| *pArray = myRange->getCellRange(); |
| OSL_TRACE("Param[%d] is %s", (int)(pArray - aParamTemp.getConstArray()), rtl::OUStringToOString( comphelper::anyToString( *pArray ), RTL_TEXTENCODING_UTF8 ).getStr() ); |
| } |
| } |
| |
| uno::Any aRet; |
| bool bAsArray = true; |
| |
| // special handing for some functions that don't work correctly in FunctionAccess |
| ScCompiler aCompiler( 0, ScAddress() ); |
| OpCode eOpCode = aCompiler.GetEnglishOpCode( FunctionName.toAsciiUpperCase() ); |
| switch( eOpCode ) |
| { |
| // ISLOGICAL does not work in array formula mode |
| case ocIsLogical: |
| { |
| if( aParamTemp.getLength() != 1 ) |
| throw lang::IllegalArgumentException(); |
| const uno::Any& rParam = aParamTemp[ 0 ]; |
| if( rParam.has< bool >() ) |
| { |
| aRet <<= true; |
| } |
| else if( rParam.has< uno::Reference< table::XCellRange > >() ) try |
| { |
| uno::Reference< sheet::XCellRangeAddressable > xRangeAddr( rParam, uno::UNO_QUERY_THROW ); |
| table::CellRangeAddress aRangeAddr = xRangeAddr->getRangeAddress(); |
| bAsArray = (aRangeAddr.StartColumn != aRangeAddr.EndColumn) || (aRangeAddr.StartRow != aRangeAddr.EndRow); |
| } |
| catch( uno::Exception& ) |
| { |
| } |
| } |
| break; |
| default:; |
| } |
| |
| if( !aRet.hasValue() ) |
| { |
| uno::Reference< lang::XMultiComponentFactory > xSMgr( mxContext->getServiceManager(), uno::UNO_QUERY_THROW ); |
| uno::Reference< sheet::XFunctionAccess > xFunctionAccess( xSMgr->createInstanceWithContext( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.sheet.FunctionAccess" ) ), mxContext ), |
| uno::UNO_QUERY_THROW ); |
| uno::Reference< beans::XPropertySet > xPropSet( xFunctionAccess, uno::UNO_QUERY_THROW ); |
| xPropSet->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsArrayFunction" ) ), uno::Any( bAsArray ) ); |
| aRet = xFunctionAccess->callFunction( FunctionName, aParamTemp ); |
| } |
| |
| /* Convert return value from double to to Boolean for some functions that |
| return Booleans. */ |
| typedef uno::Sequence< uno::Sequence< uno::Any > > AnySeqSeq; |
| if( (eOpCode == ocIsEmpty) || (eOpCode == ocIsString) || (eOpCode == ocIsNonString) || (eOpCode == ocIsLogical) || |
| (eOpCode == ocIsRef) || (eOpCode == ocIsValue) || (eOpCode == ocIsFormula) || (eOpCode == ocIsNA) || |
| (eOpCode == ocIsErr) || (eOpCode == ocIsError) || (eOpCode == ocIsEven) || (eOpCode == ocIsOdd) || |
| (eOpCode == ocAnd) || (eOpCode == ocOr) || (eOpCode == ocNot) || (eOpCode == ocTrue) || (eOpCode == ocFalse) ) |
| { |
| if( aRet.has< AnySeqSeq >() ) |
| { |
| AnySeqSeq aAnySeqSeq = aRet.get< AnySeqSeq >(); |
| for( sal_Int32 nRow = 0; nRow < aAnySeqSeq.getLength(); ++nRow ) |
| for( sal_Int32 nCol = 0; nCol < aAnySeqSeq[ nRow ].getLength(); ++nCol ) |
| lclConvertDoubleToBoolean( aAnySeqSeq[ nRow ][ nCol ] ); |
| aRet <<= aAnySeqSeq; |
| } |
| else |
| { |
| lclConvertDoubleToBoolean( aRet ); |
| } |
| } |
| |
| /* Hack/workaround (?): shorten single-row matrix to simple array, shorten |
| 1x1 matrix to single value. */ |
| if( aRet.has< AnySeqSeq >() ) |
| { |
| AnySeqSeq aAnySeqSeq = aRet.get< AnySeqSeq >(); |
| if( aAnySeqSeq.getLength() == 1 ) |
| { |
| if( aAnySeqSeq[ 0 ].getLength() == 1 ) |
| aRet = aAnySeqSeq[ 0 ][ 0 ]; |
| else |
| aRet <<= aAnySeqSeq[ 0 ]; |
| } |
| } |
| |
| #if 0 |
| // 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? |
| // To fix this issue in safe, current solution is to convert this array to a double value just for MATCH function. |
| String aUpper( FunctionName ); |
| ScCompiler aCompiler( NULL, ScAddress() ); |
| OpCode eOp = aCompiler.GetEnglishOpCode( aUpper.ToUpperAscii() ); |
| if( eOp == ocMatch ) |
| { |
| double fVal = 0.0; |
| if( aRet >>= fVal ) |
| return aRet; |
| uno::Sequence< uno::Sequence< uno::Any > > aSequence; |
| if( !( ( aRet >>= aSequence ) && ( aSequence.getLength() > 0 ) && |
| ( aSequence[0].getLength() > 0 ) && ( aSequence[0][0] >>= fVal ) ) ) |
| throw uno::RuntimeException(); |
| aRet <<= fVal; |
| } |
| #endif |
| |
| return aRet; |
| } |
| |
| void SAL_CALL |
| ScVbaWSFunction::setValue(const rtl::OUString& /*PropertyName*/, const uno::Any& /*Value*/) throw(beans::UnknownPropertyException, script::CannotConvertException, reflection::InvocationTargetException, uno::RuntimeException) |
| { |
| throw beans::UnknownPropertyException(); |
| } |
| |
| uno::Any SAL_CALL |
| ScVbaWSFunction::getValue(const rtl::OUString& /*PropertyName*/) throw(beans::UnknownPropertyException, uno::RuntimeException) |
| { |
| throw beans::UnknownPropertyException(); |
| } |
| |
| sal_Bool SAL_CALL |
| ScVbaWSFunction::hasMethod(const rtl::OUString& Name) throw(uno::RuntimeException) |
| { |
| sal_Bool bIsFound = sal_False; |
| try |
| { |
| // the function name contained in the com.sun.star.sheet.FunctionDescription service is alwayse localized. |
| // but the function name used in WorksheetFunction is a programmatic name (seems English). |
| // So m_xNameAccess->hasByName( Name ) may fail to find name when a function name has a localized name. |
| ScCompiler aCompiler( NULL, ScAddress() ); |
| if( aCompiler.IsEnglishSymbol( Name ) ) |
| bIsFound = sal_True; |
| } |
| catch( uno::Exception& /*e*/ ) |
| { |
| // failed to find name |
| } |
| return bIsFound; |
| } |
| |
| sal_Bool SAL_CALL |
| ScVbaWSFunction::hasProperty(const rtl::OUString& /*Name*/) throw(uno::RuntimeException) |
| { |
| return sal_False; |
| } |
| |
| ::rtl::OUString SAL_CALL |
| ScVbaWSFunction::getExactName( const ::rtl::OUString& aApproximateName ) throw (css::uno::RuntimeException) |
| { |
| rtl::OUString sName = aApproximateName.toAsciiUpperCase(); |
| if ( !hasMethod( sName ) ) |
| return rtl::OUString(); |
| return sName; |
| } |
| |
| rtl::OUString& |
| ScVbaWSFunction::getServiceImplName() |
| { |
| static rtl::OUString sImplName( RTL_CONSTASCII_USTRINGPARAM("ScVbaWSFunction") ); |
| return sImplName; |
| } |
| |
| uno::Sequence< rtl::OUString > |
| ScVbaWSFunction::getServiceNames() |
| { |
| static uno::Sequence< rtl::OUString > aServiceNames; |
| if ( aServiceNames.getLength() == 0 ) |
| { |
| aServiceNames.realloc( 1 ); |
| aServiceNames[ 0 ] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ooo.vba.excel.WorksheetFunction" ) ); |
| } |
| return aServiceNames; |
| } |