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(
136 "pyuno bridge: Couldn't get reflection for method ")
138 Reference
< XInterface
> () );
141 Sequence
< ParamInfo
> seqInfo
= method
->getParameterInfos();
144 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
146 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
147 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
149 // sequence must be interpreted as return value/outparameter tuple !
156 ret
.realloc( nOuts
);
157 sal_Int32 nOutsAssigned
= 0;
158 for( i
= 0 ; i
< seqInfo
.getLength() ; i
++ )
160 if( seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_OUT
||
161 seqInfo
[i
].aMode
== com::sun::star::reflection::ParamMode_INOUT
)
163 ret
[nOutsAssigned
] = (sal_Int16
) i
;
169 // guard active again !
170 m_methodOutIndexMap
[ functionName
] = ret
;
179 Any
Adapter::invoke( const OUString
&aFunctionName
,
180 const Sequence
< Any
>& aParams
,
181 Sequence
< sal_Int16
> &aOutParamIndex
,
182 Sequence
< Any
> &aOutParam
)
183 throw (IllegalArgumentException
,CannotConvertException
,InvocationTargetException
,RuntimeException
)
187 // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
188 // always handled by the adapter directly.
189 if( aParams
.getLength() == 1 && 0 == aFunctionName
.compareToAscii( "getSomething" ) )
191 Sequence
< sal_Int8
> id
;
192 if( aParams
[0] >>= id
)
193 return com::sun::star::uno::makeAny( getSomething( id
) );
197 RuntimeCargo
*cargo
= 0;
200 PyThreadAttach
guard( mInterpreter
);
202 // convert parameters to python args
203 // TODO: Out parameter
205 cargo
= runtime
.getImpl()->cargo
;
206 if( isLog( cargo
, LogLevel::CALL
) )
208 logCall( cargo
, "try uno->py[0x",
209 mWrappedObject
.get(), aFunctionName
, aParams
);
212 sal_Int32 size
= aParams
.getLength();
213 PyRef
argsTuple(PyTuple_New( size
), SAL_NO_ACQUIRE
);
215 // fill tuple with default values in case of exceptions
216 for( i
= 0 ;i
< size
; i
++ )
218 Py_INCREF( Py_None
);
219 PyTuple_SetItem( argsTuple
.get(), i
, Py_None
);
222 // convert args to python
223 for( i
= 0; i
< size
; i
++ )
225 PyRef val
= runtime
.any2PyObject( aParams
[i
] );
226 PyTuple_SetItem( argsTuple
.get(), i
, val
.getAcquired() );
230 PyRef
method(PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aFunctionName
)),
232 raiseInvocationTargetExceptionWhenNeeded( runtime
);
236 buf
.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName
);
237 buf
.appendAscii( " is not implemented at object " );
238 PyRef
str( PyObject_Repr( mWrappedObject
.get() ), SAL_NO_ACQUIRE
);
239 buf
.append(pyString2ustring(str
.get()));
240 throw IllegalArgumentException( buf
.makeStringAndClear(), Reference
< XInterface
> (),0 );
243 PyRef
pyRet( PyObject_CallObject( method
.get(), argsTuple
.get() ), SAL_NO_ACQUIRE
);
244 raiseInvocationTargetExceptionWhenNeeded( runtime
);
247 ret
= runtime
.pyObject2Any( pyRet
);
249 if( ret
.hasValue() &&
250 ret
.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE
&&
251 0 != aFunctionName
.compareToAscii( "getTypes" ) && // needed by introspection itself !
252 0 != aFunctionName
.compareToAscii( "getImplementationId" ) ) // needed by introspection itself !
254 // the sequence can either be
255 // 1) a simple sequence return value
256 // 2) a sequence, where the first element is the return value
257 // and the following elements are interpreted as the outparameter
258 // I can only decide for one solution by checking the method signature,
259 // so I need the reflection of the adapter !
260 aOutParamIndex
= getOutIndexes( aFunctionName
);
261 if( aOutParamIndex
.getLength() )
263 // out parameters exist, extract the sequence
265 if( ! ( ret
>>= seq
) )
267 throw RuntimeException(
268 (OUString("pyuno bridge: Couldn't extract out"
269 " parameters for method ")
271 Reference
< XInterface
> () );
274 if( aOutParamIndex
.getLength() +1 != seq
.getLength() )
277 buf
.append( "pyuno bridge: expected for method " );
278 buf
.append( aFunctionName
);
279 buf
.append( " one return value and " );
280 buf
.append( (sal_Int32
) aOutParamIndex
.getLength() );
281 buf
.append( " out parameters, got a sequence of " );
282 buf
.append( seq
.getLength() );
283 buf
.append( " elements as return value." );
284 throw RuntimeException(buf
.makeStringAndClear(), *this );
287 aOutParam
.realloc( aOutParamIndex
.getLength() );
289 for( i
= 0 ; i
< aOutParamIndex
.getLength() ; i
++ )
291 aOutParam
[i
] = seq
[1+i
];
294 // else { sequence is a return value !}
298 // log the reply, if desired
299 if( isLog( cargo
, LogLevel::CALL
) )
301 logReply( cargo
, "success uno->py[0x" ,
302 mWrappedObject
.get(), aFunctionName
, ret
, aOutParam
);
307 catch( const InvocationTargetException
& e
)
309 if( isLog( cargo
, LogLevel::CALL
) )
312 cargo
, "except uno->py[0x" ,
313 mWrappedObject
.get(), aFunctionName
,
314 e
.TargetException
.getValue(),e
.TargetException
.getValueType() );
318 catch( const IllegalArgumentException
& e
)
320 if( isLog( cargo
, LogLevel::CALL
) )
323 cargo
, "except uno->py[0x" ,
324 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
328 catch( const RuntimeException
& e
)
330 if( cargo
&& isLog( cargo
, LogLevel::CALL
) )
333 cargo
, "except uno->py[0x" ,
334 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
338 catch( const CannotConvertException
& e
)
340 if( isLog( cargo
, LogLevel::CALL
) )
343 cargo
, "except uno->py[0x" ,
344 mWrappedObject
.get(), aFunctionName
, &e
,getCppuType(&e
) );
351 void Adapter::setValue( const OUString
& aPropertyName
, const Any
& value
)
352 throw( UnknownPropertyException
, CannotConvertException
, InvocationTargetException
,RuntimeException
)
354 if( !hasProperty( aPropertyName
) )
357 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
358 buf
.appendAscii( " is unknown." );
359 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
362 PyThreadAttach
guard( mInterpreter
);
366 PyRef obj
= runtime
.any2PyObject( value
);
368 PyObject_SetAttrString(
369 mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
), obj
.get() );
370 raiseInvocationTargetExceptionWhenNeeded( runtime
);
373 catch( const IllegalArgumentException
& exc
)
375 throw InvocationTargetException( exc
.Message
, *this, com::sun::star::uno::makeAny( exc
) );
379 Any
Adapter::getValue( const OUString
& aPropertyName
)
380 throw ( UnknownPropertyException
, RuntimeException
)
383 PyThreadAttach
guard( mInterpreter
);
387 PyObject_GetAttrString( mWrappedObject
.get(), (char*)TO_ASCII(aPropertyName
) ),
390 raiseInvocationTargetExceptionWhenNeeded( runtime
);
394 buf
.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName
);
395 buf
.appendAscii( " is unknown." );
396 throw UnknownPropertyException( buf
.makeStringAndClear(), Reference
< XInterface
> () );
398 ret
= runtime
.pyObject2Any( pyRef
);
403 sal_Bool
Adapter::hasMethod( const OUString
& aMethodName
)
404 throw ( RuntimeException
)
406 return hasProperty( aMethodName
);
409 sal_Bool
Adapter::hasProperty( const OUString
& aPropertyName
)
410 throw ( RuntimeException
)
413 PyThreadAttach
guard( mInterpreter
);
415 bRet
= PyObject_HasAttrString(
416 mWrappedObject
.get() , (char*) TO_ASCII( aPropertyName
));
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */