/************************************************************************* * * $RCSfile: pyuno_adapter.cxx,v $ * * $Revision: 1.6 $ * * last change: $Author: hr $ $Date: 2005-02-11 16:40:12 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (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.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Ralph Thomas * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): Ralph Thomas, Joerg Budischewski * * ************************************************************************/ #include "pyuno_impl.hxx" #include #include #include #include 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 Runtime & runtime, 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 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(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(), 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( OUStringBuffer().appendAscii("pyuno bridge: Couldn't get reflection for method " ).append( functionName ).makeStringAndClear(), 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 ) ); } PyThreadAttach guard( mInterpreter ); { // convert parameters to python args // TODO: Out parameter Runtime runtime; 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.appendAscii( PyString_AsString( str.get() )); throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 ); } PYUNO_DEBUG_2( "entering python method %s\n" , (char*)TO_ASCII(aFunctionName) ); PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE ); PYUNO_DEBUG_3( "leaving python method %s %d\n" , (char*)TO_ASCII(aFunctionName) , pyRet.is() ); 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( OUStringBuffer().appendAscii( "pyuno bridge: Couldn't extract out parameters for method " ).append( aFunctionName ).makeStringAndClear(), 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 !} } } } PYUNO_DEBUG_1( "leaving Adapter::invoke normally\n" ); return ret; } void Adapter::setValue( const OUString & aPropertyName, const Any & value ) throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException) { PyThreadAttach guard( mInterpreter ); try { Runtime runtime; PyRef obj = runtime.any2PyObject( value ); if( !hasProperty( aPropertyName ) ) { OUStringBuffer buf; buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName ); buf.appendAscii( " is unknown." ); throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () ); } 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 ) { sal_Bool bRet = sal_False; PyThreadAttach guard( mInterpreter ); { bRet = PyObject_HasAttrString( mWrappedObject.get() , (char*) TO_ASCII( aPropertyName )); } return bRet; } }