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( false );
68 Sequence
<sal_Int8
> Adapter::getUnoTunnelImplementationId()
70 return g_id
.getImplementationId();
73 sal_Int64
Adapter::getSomething( const Sequence
< sal_Int8
> &id
) throw (RuntimeException
, std::exception
)
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(reinterpret_cast<PyObject
**>(&excType
), reinterpret_cast<PyObject
**>(&excValue
), reinterpret_cast<PyObject
**>(&excTraceback
));
87 Any
unoExc( runtime
.extractUnoException( excType
, excValue
, excTraceback
) );
88 throw InvocationTargetException(
89 static_cast<com::sun::star::uno::Exception
const *>(unoExc
.getValue())->Message
,
90 Reference
<XInterface
>(), unoExc
);
94 Reference
< XIntrospectionAccess
> Adapter::getIntrospection()
95 throw ( RuntimeException
, std::exception
)
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 "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" );
129 Reference
< XIdlMethod
> method
= introspection
->getMethod(
130 functionName
, com::sun::star::beans::MethodConcept::ALL
);
133 throw RuntimeException(
134 "pyuno bridge: Couldn't get reflection for method " + functionName
);
137 Sequence
< ParamInfo
> seqInfo
= method
->getParameterInfos();
140 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
142 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
143 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
145 // sequence must be interpreted as return value/outparameter tuple !
152 ret
.realloc( nOuts
);
153 sal_Int32 nOutsAssigned
= 0;
154 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
156 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
157 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
159 ret
[nOutsAssigned
] = (sal_Int16
) i
;
165 // guard active again !
166 m_methodOutIndexMap
[ functionName
] = ret
;
175 Any
Adapter::invoke( const OUString
&aFunctionName
,
176 const Sequence
< Any
>& aParams
,
177 Sequence
< sal_Int16
> &aOutParamIndex
,
178 Sequence
< Any
> &aOutParam
)
179 throw (IllegalArgumentException
,CannotConvertException
,InvocationTargetException
,RuntimeException
, std::exception
)
183 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
184 // always handled by the adapter directly.
185 if( aParams
.getLength() == 1 && aFunctionName
== "getSomething" )
187 Sequence
< sal_Int8
> id
;
188 if( aParams
[0] >>= id
)
189 return com::sun::star::uno::makeAny( getSomething( id
) );
193 RuntimeCargo
*cargo
= 0;
196 PyThreadAttach
guard( mInterpreter
);
198 // convert parameters to python args
199 // TODO: Out parameter
201 cargo
= runtime
.getImpl()->cargo
;
202 if( isLog( cargo
, LogLevel::CALL
) )
204 logCall( cargo
, "try uno->py[0x",
205 mWrappedObject
.get(), aFunctionName
, aParams
);
208 sal_Int32 size
= aParams
.getLength();
209 PyRef
argsTuple(PyTuple_New( size
), SAL_NO_ACQUIRE
, NOT_NULL
);
211 // fill tuple with default values in case of exceptions
212 for( i
= 0 ;i
< size
; i
++ )
214 Py_INCREF( Py_None
);
215 PyTuple_SetItem( argsTuple
.get(), i
, Py_None
);
218 // convert args to python
219 for( i
= 0; i
< size
; i
++ )
221 PyRef val
= runtime
.any2PyObject( aParams
[i
] );
222 PyTuple_SetItem( argsTuple
.get(), i
, val
.getAcquired() );
226 PyRef
method(PyObject_GetAttrString( mWrappedObject
.get(), TO_ASCII(aFunctionName
)),
228 raiseInvocationTargetExceptionWhenNeeded( runtime
);
232 buf
.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName
);
233 buf
.appendAscii( " is not implemented at object " );
234 PyRef
str( PyObject_Repr( mWrappedObject
.get() ), SAL_NO_ACQUIRE
);
235 buf
.append(pyString2ustring(str
.get()));
236 throw IllegalArgumentException( buf
.makeStringAndClear(), Reference
< XInterface
> (),0 );
239 PyRef
pyRet( PyObject_CallObject( method
.get(), argsTuple
.get() ), SAL_NO_ACQUIRE
);
240 raiseInvocationTargetExceptionWhenNeeded( runtime
);
243 ret
= runtime
.pyObject2Any( pyRet
);
245 if( ret
.hasValue() &&
246 ret
.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE
&&
247 aFunctionName
!= "getTypes" && // needed by introspection itself !
248 aFunctionName
!= "getImplementationId" ) // needed by introspection itself !
250 // the sequence can either be
251 // 1) a simple sequence return value
252 // 2) a sequence, where the first element is the return value
253 // and the following elements are interpreted as the outparameter
254 // I can only decide for one solution by checking the method signature,
255 // so I need the reflection of the adapter !
256 aOutParamIndex
= getOutIndexes( aFunctionName
);
257 if( aOutParamIndex
.getLength() )
259 // out parameters exist, extract the sequence
261 if( ! ( ret
>>= seq
) )
263 throw RuntimeException(
264 "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName
);
267 if( aOutParamIndex
.getLength() +1 != seq
.getLength() )
270 buf
.append( "pyuno bridge: expected for method " );
271 buf
.append( aFunctionName
);
272 buf
.append( " one return value and " );
273 buf
.append( (sal_Int32
) aOutParamIndex
.getLength() );
274 buf
.append( " out parameters, got a sequence of " );
275 buf
.append( seq
.getLength() );
276 buf
.append( " elements as return value." );
277 throw RuntimeException(buf
.makeStringAndClear(), *this );
280 aOutParam
.realloc( aOutParamIndex
.getLength() );
282 for( i
= 0 ; i
< aOutParamIndex
.getLength() ; i
++ )
284 aOutParam
[i
] = seq
[1+i
];
287 // else { sequence is a return value !}
291 // log the reply, if desired
292 if( isLog( cargo
, LogLevel::CALL
) )
294 logReply( cargo
, "success uno->py[0x" ,
295 mWrappedObject
.get(), aFunctionName
, ret
, aOutParam
);
300 catch( const InvocationTargetException
& e
)
302 if( isLog( cargo
, LogLevel::CALL
) )
305 cargo
, "except uno->py[0x" ,
306 mWrappedObject
.get(), aFunctionName
,
307 e
.TargetException
.getValue(),e
.TargetException
.getValueType() );
311 catch( const IllegalArgumentException
& e
)
313 if( isLog( cargo
, LogLevel::CALL
) )
316 cargo
, "except uno->py[0x" ,
317 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
321 catch( const RuntimeException
& e
)
323 if( cargo
&& isLog( cargo
, LogLevel::CALL
) )
326 cargo
, "except uno->py[0x" ,
327 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
331 catch( const CannotConvertException
& e
)
333 if( isLog( cargo
, LogLevel::CALL
) )
336 cargo
, "except uno->py[0x" ,
337 mWrappedObject
.get(), aFunctionName
, &e
,cppu::UnoType
<decltype(e
)>::get() );
344 void Adapter::setValue( const OUString
& aPropertyName
, const Any
& value
)
345 throw( UnknownPropertyException
, CannotConvertException
, InvocationTargetException
,RuntimeException
, std::exception
)
347 if( !hasProperty( aPropertyName
) )
350 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
351 buf
.appendAscii( " is unknown." );
352 throw UnknownPropertyException( buf
.makeStringAndClear() );
355 PyThreadAttach
guard( mInterpreter
);
359 PyRef obj
= runtime
.any2PyObject( value
);
361 PyObject_SetAttrString(
362 mWrappedObject
.get(), TO_ASCII(aPropertyName
), obj
.get() );
363 raiseInvocationTargetExceptionWhenNeeded( runtime
);
366 catch( const IllegalArgumentException
& exc
)
368 throw InvocationTargetException( exc
.Message
, *this, com::sun::star::uno::makeAny( exc
) );
372 Any
Adapter::getValue( const OUString
& aPropertyName
)
373 throw ( UnknownPropertyException
, RuntimeException
, std::exception
)
376 PyThreadAttach
guard( mInterpreter
);
380 PyObject_GetAttrString( mWrappedObject
.get(), TO_ASCII(aPropertyName
) ),
383 if (!pyRef
.is() || PyErr_Occurred())
386 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
387 buf
.appendAscii( " is unknown." );
388 throw UnknownPropertyException( buf
.makeStringAndClear() );
390 ret
= runtime
.pyObject2Any( pyRef
);
395 sal_Bool
Adapter::hasMethod( const OUString
& aMethodName
)
396 throw ( RuntimeException
, std::exception
)
398 return hasProperty( aMethodName
);
401 sal_Bool
Adapter::hasProperty( const OUString
& aPropertyName
)
402 throw ( RuntimeException
, std::exception
)
405 PyThreadAttach
guard( mInterpreter
);
407 bRet
= PyObject_HasAttrString(
408 mWrappedObject
.get() , TO_ASCII( aPropertyName
));
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */