1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: pyuno_adapter.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
30 #include "pyuno_impl.hxx"
32 #include <rtl/ustrbuf.hxx>
33 #include <rtl/strbuf.hxx>
35 #include <com/sun/star/beans/MethodConcept.hpp>
37 #include <cppuhelper/typeprovider.hxx>
39 using rtl::OUStringToOString
;
41 using rtl::OUStringBuffer
;
43 using rtl::OStringBuffer
;
45 using com::sun::star::beans::XIntrospectionAccess
;
46 using com::sun::star::beans::XIntrospection
;
47 using com::sun::star::uno::Any
;
48 using com::sun::star::uno::makeAny
;
49 using com::sun::star::uno::Reference
;
50 using com::sun::star::uno::Sequence
;
51 using com::sun::star::uno::RuntimeException
;
52 using com::sun::star::uno::XInterface
;
53 using com::sun::star::uno::Type
;
54 using com::sun::star::lang::XUnoTunnel
;
55 using com::sun::star::lang::IllegalArgumentException
;
56 using com::sun::star::beans::UnknownPropertyException
;
57 using com::sun::star::script::CannotConvertException
;
58 using com::sun::star::reflection::InvocationTargetException
;
59 using com::sun::star::reflection::XIdlMethod
;
60 using com::sun::star::reflection::ParamInfo
;
61 using com::sun::star::reflection::XIdlClass
;
63 #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
68 Adapter::Adapter( const PyRef
& ref
, const Sequence
< Type
> &types
)
69 : mWrappedObject( ref
),
70 mInterpreter( (PyThreadState_Get()->interp
) ),
76 // Problem: We don't know, if we have the python interpreter lock
77 // There is no runtime function to get to know this.
78 decreaseRefCount( mInterpreter
, mWrappedObject
.get() );
79 mWrappedObject
.scratch();
82 static cppu::OImplementationId
g_id( sal_False
);
84 Sequence
<sal_Int8
> Adapter::getUnoTunnelImplementationId()
86 return g_id
.getImplementationId();
89 sal_Int64
Adapter::getSomething( const Sequence
< sal_Int8
> &id
) throw (RuntimeException
)
91 if( id
== g_id
.getImplementationId() )
92 return reinterpret_cast<sal_Int64
>(this);
96 void raiseInvocationTargetExceptionWhenNeeded( const Runtime
&runtime
)
97 throw ( InvocationTargetException
)
99 if( PyErr_Occurred() )
101 PyRef excType
, excValue
, excTraceback
;
102 PyErr_Fetch( (PyObject
**)&excType
, (PyObject
**)&excValue
,(PyObject
**)&excTraceback
);
103 Any
unoExc( runtime
.extractUnoException( excType
, excValue
, excTraceback
) );
104 throw InvocationTargetException(
105 ((com::sun::star::uno::Exception
*)unoExc
.getValue())->Message
,
106 Reference
<XInterface
>(), unoExc
);
110 Reference
< XIntrospectionAccess
> Adapter::getIntrospection()
111 throw ( RuntimeException
)
114 return Reference
< XIntrospectionAccess
> ();
117 Sequence
< sal_Int16
> Adapter::getOutIndexes( const OUString
& functionName
)
119 Sequence
< sal_Int16
> ret
;
120 MethodOutIndexMap::const_iterator ii
= m_methodOutIndexMap
.find( functionName
);
121 if( ii
== m_methodOutIndexMap
.end() )
126 PyThreadDetach antiguard
;
128 // retrieve the adapter object again. It will be the same instance as before,
129 // (the adapter factory keeps a weak map inside, which I couldn't have outside)
130 Reference
< XInterface
> unoAdapterObject
=
131 runtime
.getImpl()->cargo
->xAdapterFactory
->createAdapter( this, mTypes
);
133 // uuuh, that's really expensive. The alternative would have been, to store
134 // an instance of the introspection at (this), but this results in a cyclic
135 // reference, which is never broken (as it is up to OOo1.1.0).
136 Reference
< XIntrospectionAccess
> introspection
=
137 runtime
.getImpl()->cargo
->xIntrospection
->inspect( makeAny( unoAdapterObject
) );
139 if( !introspection
.is() )
141 throw RuntimeException(
142 OUString( RTL_CONSTASCII_USTRINGPARAM( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ) ),
143 Reference
< XInterface
> () );
146 Reference
< XIdlMethod
> method
= introspection
->getMethod(
147 functionName
, com::sun::star::beans::MethodConcept::ALL
);
150 throw RuntimeException(
152 RTL_CONSTASCII_USTRINGPARAM(
153 "pyuno bridge: Couldn't get reflection for method "))
155 Reference
< XInterface
> () );
158 Sequence
< ParamInfo
> seqInfo
= method
->getParameterInfos();
161 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
163 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
164 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
166 // sequence must be interpreted as return value/outparameter tuple !
173 ret
.realloc( nOuts
);
174 sal_Int32 nOutsAssigned
= 0;
175 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
177 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
178 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
180 ret
[nOutsAssigned
] = (sal_Int16
) i
;
186 // guard active again !
187 m_methodOutIndexMap
[ functionName
] = ret
;
196 Any
Adapter::invoke( const OUString
&aFunctionName
,
197 const Sequence
< Any
>& aParams
,
198 Sequence
< sal_Int16
> &aOutParamIndex
,
199 Sequence
< Any
> &aOutParam
)
200 throw (IllegalArgumentException
,CannotConvertException
,InvocationTargetException
,RuntimeException
)
204 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
205 // always handled by the adapter directly.
206 if( aParams
.getLength() == 1 && 0 == aFunctionName
.compareToAscii( "getSomething" ) )
208 Sequence
< sal_Int8
> id
;
209 if( aParams
[0] >>= id
)
210 return com::sun::star::uno::makeAny( getSomething( id
) );
214 RuntimeCargo
*cargo
= 0;
217 PyThreadAttach
guard( mInterpreter
);
219 // convert parameters to python args
220 // TODO: Out parameter
222 cargo
= runtime
.getImpl()->cargo
;
223 if( isLog( cargo
, LogLevel::CALL
) )
225 logCall( cargo
, "try uno->py[0x",
226 mWrappedObject
.get(), aFunctionName
, aParams
);
229 sal_Int32 size
= aParams
.getLength();
230 PyRef
argsTuple(PyTuple_New( size
), SAL_NO_ACQUIRE
);
232 // fill tuple with default values in case of exceptions
233 for( i
= 0 ;i
< size
; i
++ )
235 Py_INCREF( Py_None
);
236 PyTuple_SetItem( argsTuple
.get(), i
, Py_None
);
239 // convert args to python
240 for( i
= 0; i
< size
; i
++ )
242 PyRef val
= runtime
.any2PyObject( aParams
[i
] );
243 PyTuple_SetItem( argsTuple
.get(), i
, val
.getAcquired() );
247 PyRef
method(PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aFunctionName
)),
249 raiseInvocationTargetExceptionWhenNeeded( runtime
);
253 buf
.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName
);
254 buf
.appendAscii( " is not implemented at object " );
255 PyRef
str( PyObject_Repr( mWrappedObject
.get() ), SAL_NO_ACQUIRE
);
256 buf
.appendAscii( PyString_AsString( str
.get() ));
257 throw IllegalArgumentException( buf
.makeStringAndClear(), Reference
< XInterface
> (),0 );
260 PyRef
pyRet( PyObject_CallObject( method
.get(), argsTuple
.get() ), SAL_NO_ACQUIRE
);
261 raiseInvocationTargetExceptionWhenNeeded( runtime
);
264 ret
= runtime
.pyObject2Any( pyRet
);
266 if( ret
.hasValue() &&
267 ret
.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE
&&
268 0 != aFunctionName
.compareToAscii( "getTypes" ) && // needed by introspection itself !
269 0 != aFunctionName
.compareToAscii( "getImplementationId" ) ) // needed by introspection itself !
271 // the sequence can either be
272 // 1) a simple sequence return value
273 // 2) a sequence, where the first element is the return value
274 // and the following elements are interpreted as the outparameter
275 // I can only decide for one solution by checking the method signature,
276 // so I need the reflection of the adapter !
277 aOutParamIndex
= getOutIndexes( aFunctionName
);
278 if( aOutParamIndex
.getLength() )
280 // out parameters exist, extract the sequence
282 if( ! ( ret
>>= seq
) )
284 throw RuntimeException(
286 RTL_CONSTASCII_USTRINGPARAM(
287 "pyuno bridge: Couldn't extract out"
288 " parameters for method "))
290 Reference
< XInterface
> () );
293 if( aOutParamIndex
.getLength() +1 != seq
.getLength() )
296 buf
.appendAscii( "pyuno bridge: expected for method " );
297 buf
.append( aFunctionName
);
298 buf
.appendAscii( " one return value and " );
299 buf
.append( (sal_Int32
) aOutParamIndex
.getLength() );
300 buf
.appendAscii( " out parameters, got a sequence of " );
301 buf
.append( seq
.getLength() );
302 buf
.appendAscii( " elements as return value." );
303 throw RuntimeException(buf
.makeStringAndClear(), *this );
306 aOutParam
.realloc( aOutParamIndex
.getLength() );
308 for( i
= 0 ; i
< aOutParamIndex
.getLength() ; i
++ )
310 aOutParam
[i
] = seq
[1+i
];
313 // else { sequence is a return value !}
317 // log the reply, if desired
318 if( isLog( cargo
, LogLevel::CALL
) )
320 logReply( cargo
, "success uno->py[0x" ,
321 mWrappedObject
.get(), aFunctionName
, ret
, aOutParam
);
326 catch(InvocationTargetException
& e
)
328 if( isLog( cargo
, LogLevel::CALL
) )
331 cargo
, "except uno->py[0x" ,
332 mWrappedObject
.get(), aFunctionName
,
333 e
.TargetException
.getValue(),e
.TargetException
.getValueType() );
337 catch( RuntimeException
& e
)
339 if( cargo
&& isLog( cargo
, LogLevel::CALL
) )
342 cargo
, "except uno->py[0x" ,
343 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
347 catch( CannotConvertException
& e
)
349 if( isLog( cargo
, LogLevel::CALL
) )
352 cargo
, "except uno->py[0x" ,
353 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
357 catch( IllegalArgumentException
& e
)
359 if( isLog( cargo
, LogLevel::CALL
) )
362 cargo
, "except uno->py[0x" ,
363 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
370 void Adapter::setValue( const OUString
& aPropertyName
, const Any
& value
)
371 throw( UnknownPropertyException
, CannotConvertException
, InvocationTargetException
,RuntimeException
)
373 PyThreadAttach
guard( mInterpreter
);
377 PyRef obj
= runtime
.any2PyObject( value
);
379 if( !hasProperty( aPropertyName
) )
382 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
383 buf
.appendAscii( " is unknown." );
384 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
387 PyObject_SetAttrString(
388 mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
), obj
.get() );
389 raiseInvocationTargetExceptionWhenNeeded( runtime
);
392 catch( IllegalArgumentException
& exc
)
394 throw InvocationTargetException( exc
.Message
, *this, com::sun::star::uno::makeAny( exc
) );
398 Any
Adapter::getValue( const OUString
& aPropertyName
)
399 throw ( UnknownPropertyException
, RuntimeException
)
402 PyThreadAttach
guard( mInterpreter
);
406 PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
) ),
409 raiseInvocationTargetExceptionWhenNeeded( runtime
);
413 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
414 buf
.appendAscii( " is unknown." );
415 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
417 ret
= runtime
.pyObject2Any( pyRef
);
422 sal_Bool
Adapter::hasMethod( const OUString
& aMethodName
)
423 throw ( RuntimeException
)
425 return hasProperty( aMethodName
);
428 sal_Bool
Adapter::hasProperty( const OUString
& aPropertyName
)
429 throw ( RuntimeException
)
432 PyThreadAttach
guard( mInterpreter
);
434 bRet
= PyObject_HasAttrString(
435 mWrappedObject
.get() , (char*) TO_ASCII( aPropertyName
));