diff options
Diffstat (limited to 'sc/source/ui/vba/vbawsfunction.cxx')
-rw-r--r-- | sc/source/ui/vba/vbawsfunction.cxx | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/sc/source/ui/vba/vbawsfunction.cxx b/sc/source/ui/vba/vbawsfunction.cxx new file mode 100644 index 000000000000..60daa7303f2e --- /dev/null +++ b/sc/source/ui/vba/vbawsfunction.cxx @@ -0,0 +1,259 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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; +} |