1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include "pyuno_impl.hxx"
21 #include <rtl/ustrbuf.hxx>
22 #include <rtl/strbuf.hxx>
24 #include <com/sun/star/beans/MethodConcept.hpp>
26 #include <cppuhelper/typeprovider.hxx>
29 using com::sun::star::beans::XIntrospectionAccess
;
30 using com::sun::star::beans::XIntrospection
;
31 using com::sun::star::uno::Any
;
32 using com::sun::star::uno::makeAny
;
33 using com::sun::star::uno::Reference
;
34 using com::sun::star::uno::Sequence
;
35 using com::sun::star::uno::RuntimeException
;
36 using com::sun::star::uno::XInterface
;
37 using com::sun::star::uno::Type
;
38 using com::sun::star::lang::XUnoTunnel
;
39 using com::sun::star::lang::IllegalArgumentException
;
40 using com::sun::star::beans::UnknownPropertyException
;
41 using com::sun::star::script::CannotConvertException
;
42 using com::sun::star::reflection::InvocationTargetException
;
43 using com::sun::star::reflection::XIdlMethod
;
44 using com::sun::star::reflection::ParamInfo
;
45 using com::sun::star::reflection::XIdlClass
;
47 #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
52 Adapter::Adapter( const PyRef
& ref
, const Sequence
< Type
> &types
)
53 : mWrappedObject( ref
),
54 mInterpreter( (PyThreadState_Get()->interp
) ),
60 // Problem: We don't know, if we have the python interpreter lock
61 // There is no runtime function to get to know this.
62 decreaseRefCount( mInterpreter
, mWrappedObject
.get() );
63 mWrappedObject
.scratch();
66 static cppu::OImplementationId
g_id( sal_False
);
68 Sequence
<sal_Int8
> Adapter::getUnoTunnelImplementationId()
70 return g_id
.getImplementationId();
73 sal_Int64
Adapter::getSomething( const Sequence
< sal_Int8
> &id
) throw (RuntimeException
)
75 if( id
== g_id
.getImplementationId() )
76 return reinterpret_cast<sal_Int64
>(this);
80 void raiseInvocationTargetExceptionWhenNeeded( const Runtime
&runtime
)
81 throw ( InvocationTargetException
)
83 if( PyErr_Occurred() )
85 PyRef excType
, excValue
, excTraceback
;
86 PyErr_Fetch( (PyObject
**)&excType
, (PyObject
**)&excValue
,(PyObject
**)&excTraceback
);
87 Any
unoExc( runtime
.extractUnoException( excType
, excValue
, excTraceback
) );
88 throw InvocationTargetException(
89 ((com::sun::star::uno::Exception
*)unoExc
.getValue())->Message
,
90 Reference
<XInterface
>(), unoExc
);
94 Reference
< XIntrospectionAccess
> Adapter::getIntrospection()
95 throw ( RuntimeException
)
98 return Reference
< XIntrospectionAccess
> ();
101 Sequence
< sal_Int16
> Adapter::getOutIndexes( const OUString
& functionName
)
103 Sequence
< sal_Int16
> ret
;
104 MethodOutIndexMap::const_iterator ii
= m_methodOutIndexMap
.find( functionName
);
105 if( ii
== m_methodOutIndexMap
.end() )
110 PyThreadDetach antiguard
;
112 // retrieve the adapter object again. It will be the same instance as before,
113 // (the adapter factory keeps a weak map inside, which I couldn't have outside)
114 Reference
< XInterface
> unoAdapterObject
=
115 runtime
.getImpl()->cargo
->xAdapterFactory
->createAdapter( this, mTypes
);
117 // uuuh, that's really expensive. The alternative would have been, to store
118 // an instance of the introspection at (this), but this results in a cyclic
119 // reference, which is never broken (as it is up to OOo1.1.0).
120 Reference
< XIntrospectionAccess
> introspection
=
121 runtime
.getImpl()->cargo
->xIntrospection
->inspect( makeAny( unoAdapterObject
) );
123 if( !introspection
.is() )
125 throw RuntimeException(
126 OUString( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ),
127 Reference
< XInterface
> () );
130 Reference
< XIdlMethod
> method
= introspection
->getMethod(
131 functionName
, com::sun::star::beans::MethodConcept::ALL
);
134 throw RuntimeException(
135 "pyuno bridge: Couldn't get reflection for method " + functionName
,
136 Reference
< XInterface
> () );
139 Sequence
< ParamInfo
> seqInfo
= method
->getParameterInfos();
142 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
144 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
145 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
147 // sequence must be interpreted as return value/outparameter tuple !
154 ret
.realloc( nOuts
);
155 sal_Int32 nOutsAssigned
= 0;
156 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
158 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
159 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
161 ret
[nOutsAssigned
] = (sal_Int16
) i
;
167 // guard active again !
168 m_methodOutIndexMap
[ functionName
] = ret
;
177 Any
Adapter::invoke( const OUString
&aFunctionName
,
178 const Sequence
< Any
>& aParams
,
179 Sequence
< sal_Int16
> &aOutParamIndex
,
180 Sequence
< Any
> &aOutParam
)
181 throw (IllegalArgumentException
,CannotConvertException
,InvocationTargetException
,RuntimeException
)
185 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
186 // always handled by the adapter directly.
187 if( aParams
.getLength() == 1 && aFunctionName
.equalsAscii( "getSomething" ) )
189 Sequence
< sal_Int8
> id
;
190 if( aParams
[0] >>= id
)
191 return com::sun::star::uno::makeAny( getSomething( id
) );
195 RuntimeCargo
*cargo
= 0;
198 PyThreadAttach
guard( mInterpreter
);
200 // convert parameters to python args
201 // TODO: Out parameter
203 cargo
= runtime
.getImpl()->cargo
;
204 if( isLog( cargo
, LogLevel::CALL
) )
206 logCall( cargo
, "try uno->py[0x",
207 mWrappedObject
.get(), aFunctionName
, aParams
);
210 sal_Int32 size
= aParams
.getLength();
211 PyRef
argsTuple(PyTuple_New( size
), SAL_NO_ACQUIRE
);
213 // fill tuple with default values in case of exceptions
214 for( i
= 0 ;i
< size
; i
++ )
216 Py_INCREF( Py_None
);
217 PyTuple_SetItem( argsTuple
.get(), i
, Py_None
);
220 // convert args to python
221 for( i
= 0; i
< size
; i
++ )
223 PyRef val
= runtime
.any2PyObject( aParams
[i
] );
224 PyTuple_SetItem( argsTuple
.get(), i
, val
.getAcquired() );
228 PyRef
method(PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aFunctionName
)),
230 raiseInvocationTargetExceptionWhenNeeded( runtime
);
234 buf
.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName
);
235 buf
.appendAscii( " is not implemented at object " );
236 PyRef
str( PyObject_Repr( mWrappedObject
.get() ), SAL_NO_ACQUIRE
);
237 buf
.append(pyString2ustring(str
.get()));
238 throw IllegalArgumentException( buf
.makeStringAndClear(), Reference
< XInterface
> (),0 );
241 PyRef
pyRet( PyObject_CallObject( method
.get(), argsTuple
.get() ), SAL_NO_ACQUIRE
);
242 raiseInvocationTargetExceptionWhenNeeded( runtime
);
245 ret
= runtime
.pyObject2Any( pyRet
);
247 if( ret
.hasValue() &&
248 ret
.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE
&&
249 ! aFunctionName
.equalsAscii( "getTypes" ) && // needed by introspection itself !
250 ! aFunctionName
.equalsAscii( "getImplementationId" ) ) // needed by introspection itself !
252 // the sequence can either be
253 // 1) a simple sequence return value
254 // 2) a sequence, where the first element is the return value
255 // and the following elements are interpreted as the outparameter
256 // I can only decide for one solution by checking the method signature,
257 // so I need the reflection of the adapter !
258 aOutParamIndex
= getOutIndexes( aFunctionName
);
259 if( aOutParamIndex
.getLength() )
261 // out parameters exist, extract the sequence
263 if( ! ( ret
>>= seq
) )
265 throw RuntimeException(
266 "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName
,
267 Reference
< XInterface
> () );
270 if( aOutParamIndex
.getLength() +1 != seq
.getLength() )
273 buf
.append( "pyuno bridge: expected for method " );
274 buf
.append( aFunctionName
);
275 buf
.append( " one return value and " );
276 buf
.append( (sal_Int32
) aOutParamIndex
.getLength() );
277 buf
.append( " out parameters, got a sequence of " );
278 buf
.append( seq
.getLength() );
279 buf
.append( " elements as return value." );
280 throw RuntimeException(buf
.makeStringAndClear(), *this );
283 aOutParam
.realloc( aOutParamIndex
.getLength() );
285 for( i
= 0 ; i
< aOutParamIndex
.getLength() ; i
++ )
287 aOutParam
[i
] = seq
[1+i
];
290 // else { sequence is a return value !}
294 // log the reply, if desired
295 if( isLog( cargo
, LogLevel::CALL
) )
297 logReply( cargo
, "success uno->py[0x" ,
298 mWrappedObject
.get(), aFunctionName
, ret
, aOutParam
);
303 catch( const InvocationTargetException
& e
)
305 if( isLog( cargo
, LogLevel::CALL
) )
308 cargo
, "except uno->py[0x" ,
309 mWrappedObject
.get(), aFunctionName
,
310 e
.TargetException
.getValue(),e
.TargetException
.getValueType() );
314 catch( const IllegalArgumentException
& e
)
316 if( isLog( cargo
, LogLevel::CALL
) )
319 cargo
, "except uno->py[0x" ,
320 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
324 catch( const RuntimeException
& e
)
326 if( cargo
&& isLog( cargo
, LogLevel::CALL
) )
329 cargo
, "except uno->py[0x" ,
330 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
334 catch( const CannotConvertException
& e
)
336 if( isLog( cargo
, LogLevel::CALL
) )
339 cargo
, "except uno->py[0x" ,
340 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
347 void Adapter::setValue( const OUString
& aPropertyName
, const Any
& value
)
348 throw( UnknownPropertyException
, CannotConvertException
, InvocationTargetException
,RuntimeException
)
350 if( !hasProperty( aPropertyName
) )
353 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
354 buf
.appendAscii( " is unknown." );
355 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
358 PyThreadAttach
guard( mInterpreter
);
362 PyRef obj
= runtime
.any2PyObject( value
);
364 PyObject_SetAttrString(
365 mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
), obj
.get() );
366 raiseInvocationTargetExceptionWhenNeeded( runtime
);
369 catch( const IllegalArgumentException
& exc
)
371 throw InvocationTargetException( exc
.Message
, *this, com::sun::star::uno::makeAny( exc
) );
375 Any
Adapter::getValue( const OUString
& aPropertyName
)
376 throw ( UnknownPropertyException
, RuntimeException
)
379 PyThreadAttach
guard( mInterpreter
);
383 PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
) ),
386 raiseInvocationTargetExceptionWhenNeeded( runtime
);
390 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
391 buf
.appendAscii( " is unknown." );
392 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
394 ret
= runtime
.pyObject2Any( pyRef
);
399 sal_Bool
Adapter::hasMethod( const OUString
& aMethodName
)
400 throw ( RuntimeException
)
402 return hasProperty( aMethodName
);
405 sal_Bool
Adapter::hasProperty( const OUString
& aPropertyName
)
406 throw ( RuntimeException
)
409 PyThreadAttach
guard( mInterpreter
);
411 bRet
= PyObject_HasAttrString(
412 mWrappedObject
.get() , (char*) TO_ASCII( aPropertyName
));
419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */