| /************************************************************** |
| * |
| * 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 "pyuno_impl.hxx" |
| |
| #include <rtl/ustrbuf.hxx> |
| #include <rtl/strbuf.hxx> |
| |
| #include <com/sun/star/beans/MethodConcept.hpp> |
| |
| #include <cppuhelper/typeprovider.hxx> |
| |
| using rtl::OUStringToOString; |
| using rtl::OUString; |
| using rtl::OUStringBuffer; |
| using rtl::OString; |
| using rtl::OStringBuffer; |
| |
| using com::sun::star::beans::XIntrospectionAccess; |
| using com::sun::star::beans::XIntrospection; |
| using com::sun::star::uno::Any; |
| using com::sun::star::uno::makeAny; |
| using com::sun::star::uno::Reference; |
| using com::sun::star::uno::Sequence; |
| using com::sun::star::uno::RuntimeException; |
| using com::sun::star::uno::XInterface; |
| using com::sun::star::uno::Type; |
| using com::sun::star::lang::XUnoTunnel; |
| using com::sun::star::lang::IllegalArgumentException; |
| using com::sun::star::beans::UnknownPropertyException; |
| using com::sun::star::script::CannotConvertException; |
| using com::sun::star::reflection::InvocationTargetException; |
| using com::sun::star::reflection::XIdlMethod; |
| using com::sun::star::reflection::ParamInfo; |
| using com::sun::star::reflection::XIdlClass; |
| |
| #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr() |
| |
| namespace pyuno |
| { |
| |
| Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types ) |
| : mWrappedObject( ref ), |
| mInterpreter( (PyThreadState_Get()->interp) ), |
| mTypes( types ) |
| {} |
| |
| Adapter::~Adapter() |
| { |
| // Problem: We don't know, if we have the python interpreter lock |
| // There is no runtime function to get to know this. |
| decreaseRefCount( mInterpreter, mWrappedObject.get() ); |
| mWrappedObject.scratch(); |
| } |
| |
| static cppu::OImplementationId g_id( sal_False ); |
| |
| Sequence<sal_Int8> Adapter::getUnoTunnelImplementationId() |
| { |
| return g_id.getImplementationId(); |
| } |
| |
| sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id) throw (RuntimeException) |
| { |
| if( id == g_id.getImplementationId() ) |
| return reinterpret_cast<sal_Int64>(this); |
| return 0; |
| } |
| |
| void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime ) |
| throw ( InvocationTargetException ) |
| { |
| if( PyErr_Occurred() ) |
| { |
| PyRef excType, excValue, excTraceback; |
| PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback); |
| Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) ); |
| throw InvocationTargetException( |
| ((com::sun::star::uno::Exception*)unoExc.getValue())->Message, |
| Reference<XInterface>(), unoExc ); |
| } |
| } |
| |
| Reference< XIntrospectionAccess > Adapter::getIntrospection() |
| throw ( RuntimeException ) |
| { |
| // not supported |
| return Reference< XIntrospectionAccess > (); |
| } |
| |
| Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName ) |
| { |
| Sequence< sal_Int16 > ret; |
| MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName ); |
| if( ii == m_methodOutIndexMap.end() ) |
| { |
| |
| Runtime runtime; |
| { |
| PyThreadDetach antiguard; |
| |
| // retrieve the adapter object again. It will be the same instance as before, |
| // (the adapter factory keeps a weak map inside, which I couldn't have outside) |
| Reference< XInterface > unoAdapterObject = |
| runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes ); |
| |
| // uuuh, that's really expensive. The alternative would have been, to store |
| // an instance of the introspection at (this), but this results in a cyclic |
| // reference, which is never broken (as it is up to OOo1.1.0). |
| Reference< XIntrospectionAccess > introspection = |
| runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) ); |
| |
| if( !introspection.is() ) |
| { |
| throw RuntimeException( |
| OUString( RTL_CONSTASCII_USTRINGPARAM( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ) ), |
| Reference< XInterface > () ); |
| } |
| |
| Reference< XIdlMethod > method = introspection->getMethod( |
| functionName, com::sun::star::beans::MethodConcept::ALL ); |
| if( ! method.is( ) ) |
| { |
| throw RuntimeException( |
| (OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "pyuno bridge: Couldn't get reflection for method ")) |
| + functionName), |
| Reference< XInterface > () ); |
| } |
| |
| Sequence< ParamInfo > seqInfo = method->getParameterInfos(); |
| int i; |
| int nOuts = 0; |
| for( i = 0 ; i < seqInfo.getLength() ; i ++ ) |
| { |
| if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT || |
| seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT ) |
| { |
| // sequence must be interpreted as return value/outparameter tuple ! |
| nOuts ++; |
| } |
| } |
| |
| if( nOuts ) |
| { |
| ret.realloc( nOuts ); |
| sal_Int32 nOutsAssigned = 0; |
| for( i = 0 ; i < seqInfo.getLength() ; i ++ ) |
| { |
| if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT || |
| seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT ) |
| { |
| ret[nOutsAssigned] = (sal_Int16) i; |
| nOutsAssigned ++; |
| } |
| } |
| } |
| } |
| // guard active again ! |
| m_methodOutIndexMap[ functionName ] = ret; |
| } |
| else |
| { |
| ret = ii->second; |
| } |
| return ret; |
| } |
| |
| Any Adapter::invoke( const OUString &aFunctionName, |
| const Sequence< Any >& aParams, |
| Sequence< sal_Int16 > &aOutParamIndex, |
| Sequence< Any > &aOutParam) |
| throw (IllegalArgumentException,CannotConvertException,InvocationTargetException,RuntimeException) |
| { |
| Any ret; |
| |
| // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is |
| // always handled by the adapter directly. |
| if( aParams.getLength() == 1 && 0 == aFunctionName.compareToAscii( "getSomething" ) ) |
| { |
| Sequence< sal_Int8 > id; |
| if( aParams[0] >>= id ) |
| return com::sun::star::uno::makeAny( getSomething( id ) ); |
| |
| } |
| |
| RuntimeCargo *cargo = 0; |
| try |
| { |
| PyThreadAttach guard( mInterpreter ); |
| { |
| // convert parameters to python args |
| // TODO: Out parameter |
| Runtime runtime; |
| cargo = runtime.getImpl()->cargo; |
| if( isLog( cargo, LogLevel::CALL ) ) |
| { |
| logCall( cargo, "try uno->py[0x", |
| mWrappedObject.get(), aFunctionName, aParams ); |
| } |
| |
| sal_Int32 size = aParams.getLength(); |
| PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE ); |
| int i; |
| // fill tuple with default values in case of exceptions |
| for( i = 0 ;i < size ; i ++ ) |
| { |
| Py_INCREF( Py_None ); |
| PyTuple_SetItem( argsTuple.get(), i, Py_None ); |
| } |
| |
| // convert args to python |
| for( i = 0; i < size ; i ++ ) |
| { |
| PyRef val = runtime.any2PyObject( aParams[i] ); |
| PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() ); |
| } |
| |
| // get callable |
| PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)), |
| SAL_NO_ACQUIRE); |
| raiseInvocationTargetExceptionWhenNeeded( runtime); |
| if( !method.is() ) |
| { |
| OUStringBuffer buf; |
| buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName ); |
| buf.appendAscii( " is not implemented at object " ); |
| PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE ); |
| buf.append( pyString2ustring( str.get() ) ); |
| throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 ); |
| } |
| |
| PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE ); |
| raiseInvocationTargetExceptionWhenNeeded( runtime); |
| if( pyRet.is() ) |
| { |
| ret = runtime.pyObject2Any( pyRet ); |
| |
| if( ret.hasValue() && |
| ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE && |
| 0 != aFunctionName.compareToAscii( "getTypes" ) && // needed by introspection itself ! |
| 0 != aFunctionName.compareToAscii( "getImplementationId" ) ) // needed by introspection itself ! |
| { |
| // the sequence can either be |
| // 1) a simple sequence return value |
| // 2) a sequence, where the first element is the return value |
| // and the following elements are interpreted as the outparameter |
| // I can only decide for one solution by checking the method signature, |
| // so I need the reflection of the adapter ! |
| aOutParamIndex = getOutIndexes( aFunctionName ); |
| if( aOutParamIndex.getLength() ) |
| { |
| // out parameters exist, extract the sequence |
| Sequence< Any > seq; |
| if( ! ( ret >>= seq ) ) |
| { |
| throw RuntimeException( |
| (OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "pyuno bridge: Couldn't extract out" |
| " parameters for method ")) |
| + aFunctionName), |
| Reference< XInterface > () ); |
| } |
| |
| if( aOutParamIndex.getLength() +1 != seq.getLength() ) |
| { |
| OUStringBuffer buf; |
| buf.appendAscii( "pyuno bridge: expected for method " ); |
| buf.append( aFunctionName ); |
| buf.appendAscii( " one return value and " ); |
| buf.append( (sal_Int32) aOutParamIndex.getLength() ); |
| buf.appendAscii( " out parameters, got a sequence of " ); |
| buf.append( seq.getLength() ); |
| buf.appendAscii( " elements as return value." ); |
| throw RuntimeException(buf.makeStringAndClear(), *this ); |
| } |
| |
| aOutParam.realloc( aOutParamIndex.getLength() ); |
| ret = seq[0]; |
| for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ ) |
| { |
| aOutParam[i] = seq[1+i]; |
| } |
| } |
| // else { sequence is a return value !} |
| } |
| } |
| |
| // log the reply, if desired |
| if( isLog( cargo, LogLevel::CALL ) ) |
| { |
| logReply( cargo, "success uno->py[0x" , |
| mWrappedObject.get(), aFunctionName, ret, aOutParam ); |
| } |
| } |
| |
| } |
| catch(InvocationTargetException & e ) |
| { |
| if( isLog( cargo, LogLevel::CALL ) ) |
| { |
| logException( |
| cargo, "except uno->py[0x" , |
| mWrappedObject.get(), aFunctionName, |
| e.TargetException.getValue(),e.TargetException.getValueType() ); |
| } |
| throw; |
| } |
| catch( RuntimeException & e ) |
| { |
| if( cargo && isLog( cargo, LogLevel::CALL ) ) |
| { |
| logException( |
| cargo, "except uno->py[0x" , |
| mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); |
| } |
| throw; |
| } |
| catch( CannotConvertException & e ) |
| { |
| if( isLog( cargo, LogLevel::CALL ) ) |
| { |
| logException( |
| cargo, "except uno->py[0x" , |
| mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); |
| } |
| throw; |
| } |
| catch( IllegalArgumentException & e ) |
| { |
| if( isLog( cargo, LogLevel::CALL ) ) |
| { |
| logException( |
| cargo, "except uno->py[0x" , |
| mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) ); |
| } |
| throw; |
| } |
| return ret; |
| } |
| |
| void Adapter::setValue( const OUString & aPropertyName, const Any & value ) |
| throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException) |
| { |
| if( !hasProperty( aPropertyName ) ) |
| { |
| OUStringBuffer buf; |
| buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName ); |
| buf.appendAscii( " is unknown." ); |
| throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () ); |
| } |
| |
| PyThreadAttach guard( mInterpreter ); |
| try |
| { |
| Runtime runtime; |
| PyRef obj = runtime.any2PyObject( value ); |
| |
| PyObject_SetAttrString( |
| mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() ); |
| raiseInvocationTargetExceptionWhenNeeded( runtime); |
| |
| } |
| catch( IllegalArgumentException & exc ) |
| { |
| throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) ); |
| } |
| } |
| |
| Any Adapter::getValue( const OUString & aPropertyName ) |
| throw ( UnknownPropertyException, RuntimeException ) |
| { |
| Any ret; |
| PyThreadAttach guard( mInterpreter ); |
| { |
| Runtime runtime; |
| PyRef pyRef( |
| PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ), |
| SAL_NO_ACQUIRE ); |
| |
| raiseInvocationTargetExceptionWhenNeeded( runtime); |
| if( !pyRef.is() ) |
| { |
| OUStringBuffer buf; |
| buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName ); |
| buf.appendAscii( " is unknown." ); |
| throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () ); |
| } |
| ret = runtime.pyObject2Any( pyRef ); |
| } |
| return ret; |
| } |
| |
| sal_Bool Adapter::hasMethod( const OUString & aMethodName ) |
| throw ( RuntimeException ) |
| { |
| return hasProperty( aMethodName ); |
| } |
| |
| sal_Bool Adapter::hasProperty( const OUString & aPropertyName ) |
| throw ( RuntimeException ) |
| { |
| bool bRet = false; |
| PyThreadAttach guard( mInterpreter ); |
| { |
| bRet = PyObject_HasAttrString( |
| mWrappedObject.get() , (char*) TO_ASCII( aPropertyName )); |
| } |
| return bRet; |
| } |
| |
| } |