Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 : #include "pyuno_impl.hxx"
20 :
21 : #include <rtl/ustrbuf.hxx>
22 : #include <rtl/strbuf.hxx>
23 :
24 : #include <com/sun/star/beans/MethodConcept.hpp>
25 :
26 : #include <cppuhelper/typeprovider.hxx>
27 :
28 : using rtl::OUStringToOString;
29 : using rtl::OUString;
30 : using rtl::OUStringBuffer;
31 : using rtl::OString;
32 : using rtl::OStringBuffer;
33 :
34 : using com::sun::star::beans::XIntrospectionAccess;
35 : using com::sun::star::beans::XIntrospection;
36 : using com::sun::star::uno::Any;
37 : using com::sun::star::uno::makeAny;
38 : using com::sun::star::uno::Reference;
39 : using com::sun::star::uno::Sequence;
40 : using com::sun::star::uno::RuntimeException;
41 : using com::sun::star::uno::XInterface;
42 : using com::sun::star::uno::Type;
43 : using com::sun::star::lang::XUnoTunnel;
44 : using com::sun::star::lang::IllegalArgumentException;
45 : using com::sun::star::beans::UnknownPropertyException;
46 : using com::sun::star::script::CannotConvertException;
47 : using com::sun::star::reflection::InvocationTargetException;
48 : using com::sun::star::reflection::XIdlMethod;
49 : using com::sun::star::reflection::ParamInfo;
50 : using com::sun::star::reflection::XIdlClass;
51 :
52 : #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
53 :
54 : namespace pyuno
55 : {
56 :
57 0 : Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types )
58 : : mWrappedObject( ref ),
59 0 : mInterpreter( (PyThreadState_Get()->interp) ),
60 0 : mTypes( types )
61 0 : {}
62 :
63 0 : Adapter::~Adapter()
64 : {
65 : // Problem: We don't know, if we have the python interpreter lock
66 : // There is no runtime function to get to know this.
67 0 : decreaseRefCount( mInterpreter, mWrappedObject.get() );
68 0 : mWrappedObject.scratch();
69 0 : }
70 :
71 0 : static cppu::OImplementationId g_id( sal_False );
72 :
73 0 : Sequence<sal_Int8> Adapter::getUnoTunnelImplementationId()
74 : {
75 0 : return g_id.getImplementationId();
76 : }
77 :
78 0 : sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id) throw (RuntimeException)
79 : {
80 0 : if( id == g_id.getImplementationId() )
81 0 : return reinterpret_cast<sal_Int64>(this);
82 0 : return 0;
83 : }
84 :
85 0 : void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime )
86 : throw ( InvocationTargetException )
87 : {
88 0 : if( PyErr_Occurred() )
89 : {
90 0 : PyRef excType, excValue, excTraceback;
91 0 : PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
92 0 : Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
93 : throw InvocationTargetException(
94 0 : ((com::sun::star::uno::Exception*)unoExc.getValue())->Message,
95 0 : Reference<XInterface>(), unoExc );
96 : }
97 0 : }
98 :
99 0 : Reference< XIntrospectionAccess > Adapter::getIntrospection()
100 : throw ( RuntimeException )
101 : {
102 : // not supported
103 0 : return Reference< XIntrospectionAccess > ();
104 : }
105 :
106 0 : Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
107 : {
108 0 : Sequence< sal_Int16 > ret;
109 0 : MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
110 0 : if( ii == m_methodOutIndexMap.end() )
111 : {
112 :
113 0 : Runtime runtime;
114 : {
115 0 : PyThreadDetach antiguard;
116 :
117 : // retrieve the adapter object again. It will be the same instance as before,
118 : // (the adapter factory keeps a weak map inside, which I couldn't have outside)
119 : Reference< XInterface > unoAdapterObject =
120 0 : runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
121 :
122 : // uuuh, that's really expensive. The alternative would have been, to store
123 : // an instance of the introspection at (this), but this results in a cyclic
124 : // reference, which is never broken (as it is up to OOo1.1.0).
125 : Reference< XIntrospectionAccess > introspection =
126 0 : runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) );
127 :
128 0 : if( !introspection.is() )
129 : {
130 : throw RuntimeException(
131 : OUString( RTL_CONSTASCII_USTRINGPARAM( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ) ),
132 0 : Reference< XInterface > () );
133 : }
134 :
135 0 : Reference< XIdlMethod > method = introspection->getMethod(
136 0 : functionName, com::sun::star::beans::MethodConcept::ALL );
137 0 : if( ! method.is( ) )
138 : {
139 : throw RuntimeException(
140 : (OUString(
141 : RTL_CONSTASCII_USTRINGPARAM(
142 : "pyuno bridge: Couldn't get reflection for method "))
143 0 : + functionName),
144 0 : Reference< XInterface > () );
145 : }
146 :
147 0 : Sequence< ParamInfo > seqInfo = method->getParameterInfos();
148 : int i;
149 0 : int nOuts = 0;
150 0 : for( i = 0 ; i < seqInfo.getLength() ; i ++ )
151 : {
152 0 : if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
153 0 : seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
154 : {
155 : // sequence must be interpreted as return value/outparameter tuple !
156 0 : nOuts ++;
157 : }
158 : }
159 :
160 0 : if( nOuts )
161 : {
162 0 : ret.realloc( nOuts );
163 0 : sal_Int32 nOutsAssigned = 0;
164 0 : for( i = 0 ; i < seqInfo.getLength() ; i ++ )
165 : {
166 0 : if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
167 0 : seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
168 : {
169 0 : ret[nOutsAssigned] = (sal_Int16) i;
170 0 : nOutsAssigned ++;
171 : }
172 : }
173 0 : }
174 : }
175 : // guard active again !
176 0 : m_methodOutIndexMap[ functionName ] = ret;
177 : }
178 : else
179 : {
180 0 : ret = ii->second;
181 : }
182 0 : return ret;
183 : }
184 :
185 0 : Any Adapter::invoke( const OUString &aFunctionName,
186 : const Sequence< Any >& aParams,
187 : Sequence< sal_Int16 > &aOutParamIndex,
188 : Sequence< Any > &aOutParam)
189 : throw (IllegalArgumentException,CannotConvertException,InvocationTargetException,RuntimeException)
190 : {
191 0 : Any ret;
192 :
193 : // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
194 : // always handled by the adapter directly.
195 0 : if( aParams.getLength() == 1 && 0 == aFunctionName.compareToAscii( "getSomething" ) )
196 : {
197 0 : Sequence< sal_Int8 > id;
198 0 : if( aParams[0] >>= id )
199 0 : return com::sun::star::uno::makeAny( getSomething( id ) );
200 :
201 : }
202 :
203 0 : RuntimeCargo *cargo = 0;
204 : try
205 : {
206 0 : PyThreadAttach guard( mInterpreter );
207 : {
208 : // convert parameters to python args
209 : // TODO: Out parameter
210 0 : Runtime runtime;
211 0 : cargo = runtime.getImpl()->cargo;
212 0 : if( isLog( cargo, LogLevel::CALL ) )
213 : {
214 : logCall( cargo, "try uno->py[0x",
215 0 : mWrappedObject.get(), aFunctionName, aParams );
216 : }
217 :
218 0 : sal_Int32 size = aParams.getLength();
219 0 : PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE );
220 : int i;
221 : // fill tuple with default values in case of exceptions
222 0 : for( i = 0 ;i < size ; i ++ )
223 : {
224 0 : Py_INCREF( Py_None );
225 0 : PyTuple_SetItem( argsTuple.get(), i, Py_None );
226 : }
227 :
228 : // convert args to python
229 0 : for( i = 0; i < size ; i ++ )
230 : {
231 0 : PyRef val = runtime.any2PyObject( aParams[i] );
232 0 : PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
233 0 : }
234 :
235 : // get callable
236 0 : PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)),
237 0 : SAL_NO_ACQUIRE);
238 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
239 0 : if( !method.is() )
240 : {
241 0 : OUStringBuffer buf;
242 0 : buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName );
243 0 : buf.appendAscii( " is not implemented at object " );
244 0 : PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
245 0 : buf.append(pyString2ustring(str.get()));
246 0 : throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 );
247 : }
248 :
249 0 : PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
250 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
251 0 : if( pyRet.is() )
252 : {
253 0 : ret = runtime.pyObject2Any( pyRet );
254 :
255 0 : if( ret.hasValue() &&
256 0 : ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE &&
257 0 : 0 != aFunctionName.compareToAscii( "getTypes" ) && // needed by introspection itself !
258 0 : 0 != aFunctionName.compareToAscii( "getImplementationId" ) ) // needed by introspection itself !
259 : {
260 : // the sequence can either be
261 : // 1) a simple sequence return value
262 : // 2) a sequence, where the first element is the return value
263 : // and the following elements are interpreted as the outparameter
264 : // I can only decide for one solution by checking the method signature,
265 : // so I need the reflection of the adapter !
266 0 : aOutParamIndex = getOutIndexes( aFunctionName );
267 0 : if( aOutParamIndex.getLength() )
268 : {
269 : // out parameters exist, extract the sequence
270 0 : Sequence< Any > seq;
271 0 : if( ! ( ret >>= seq ) )
272 : {
273 : throw RuntimeException(
274 : (OUString(
275 : RTL_CONSTASCII_USTRINGPARAM(
276 : "pyuno bridge: Couldn't extract out"
277 : " parameters for method "))
278 0 : + aFunctionName),
279 0 : Reference< XInterface > () );
280 : }
281 :
282 0 : if( aOutParamIndex.getLength() +1 != seq.getLength() )
283 : {
284 0 : OUStringBuffer buf;
285 0 : buf.appendAscii( "pyuno bridge: expected for method " );
286 0 : buf.append( aFunctionName );
287 0 : buf.appendAscii( " one return value and " );
288 0 : buf.append( (sal_Int32) aOutParamIndex.getLength() );
289 0 : buf.appendAscii( " out parameters, got a sequence of " );
290 0 : buf.append( seq.getLength() );
291 0 : buf.appendAscii( " elements as return value." );
292 0 : throw RuntimeException(buf.makeStringAndClear(), *this );
293 : }
294 :
295 0 : aOutParam.realloc( aOutParamIndex.getLength() );
296 0 : ret = seq[0];
297 0 : for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ )
298 : {
299 0 : aOutParam[i] = seq[1+i];
300 0 : }
301 : }
302 : // else { sequence is a return value !}
303 : }
304 : }
305 :
306 : // log the reply, if desired
307 0 : if( isLog( cargo, LogLevel::CALL ) )
308 : {
309 : logReply( cargo, "success uno->py[0x" ,
310 0 : mWrappedObject.get(), aFunctionName, ret, aOutParam );
311 0 : }
312 0 : }
313 :
314 : }
315 0 : catch( const InvocationTargetException & e )
316 : {
317 0 : if( isLog( cargo, LogLevel::CALL ) )
318 : {
319 : logException(
320 : cargo, "except uno->py[0x" ,
321 0 : mWrappedObject.get(), aFunctionName,
322 0 : e.TargetException.getValue(),e.TargetException.getValueType() );
323 : }
324 0 : throw;
325 : }
326 0 : catch( const IllegalArgumentException & e )
327 : {
328 0 : if( isLog( cargo, LogLevel::CALL ) )
329 : {
330 : logException(
331 : cargo, "except uno->py[0x" ,
332 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
333 : }
334 0 : throw;
335 : }
336 0 : catch( const RuntimeException & e )
337 : {
338 0 : if( cargo && isLog( cargo, LogLevel::CALL ) )
339 : {
340 : logException(
341 : cargo, "except uno->py[0x" ,
342 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
343 : }
344 0 : throw;
345 : }
346 0 : catch( const CannotConvertException & e )
347 : {
348 0 : if( isLog( cargo, LogLevel::CALL ) )
349 : {
350 : logException(
351 : cargo, "except uno->py[0x" ,
352 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
353 : }
354 0 : throw;
355 : }
356 0 : return ret;
357 : }
358 :
359 0 : void Adapter::setValue( const OUString & aPropertyName, const Any & value )
360 : throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException)
361 : {
362 0 : if( !hasProperty( aPropertyName ) )
363 : {
364 0 : OUStringBuffer buf;
365 0 : buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
366 0 : buf.appendAscii( " is unknown." );
367 0 : throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
368 : }
369 :
370 0 : PyThreadAttach guard( mInterpreter );
371 : try
372 : {
373 0 : Runtime runtime;
374 0 : PyRef obj = runtime.any2PyObject( value );
375 :
376 : PyObject_SetAttrString(
377 0 : mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() );
378 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
379 :
380 : }
381 0 : catch( const IllegalArgumentException & exc )
382 : {
383 0 : throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) );
384 0 : }
385 0 : }
386 :
387 0 : Any Adapter::getValue( const OUString & aPropertyName )
388 : throw ( UnknownPropertyException, RuntimeException )
389 : {
390 0 : Any ret;
391 0 : PyThreadAttach guard( mInterpreter );
392 : {
393 0 : Runtime runtime;
394 : PyRef pyRef(
395 0 : PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ),
396 0 : SAL_NO_ACQUIRE );
397 :
398 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
399 0 : if( !pyRef.is() )
400 : {
401 0 : OUStringBuffer buf;
402 0 : buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
403 0 : buf.appendAscii( " is unknown." );
404 0 : throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
405 : }
406 0 : ret = runtime.pyObject2Any( pyRef );
407 : }
408 0 : return ret;
409 : }
410 :
411 0 : sal_Bool Adapter::hasMethod( const OUString & aMethodName )
412 : throw ( RuntimeException )
413 : {
414 0 : return hasProperty( aMethodName );
415 : }
416 :
417 0 : sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
418 : throw ( RuntimeException )
419 : {
420 0 : bool bRet = false;
421 0 : PyThreadAttach guard( mInterpreter );
422 : {
423 : bRet = PyObject_HasAttrString(
424 0 : mWrappedObject.get() , (char*) TO_ASCII( aPropertyName ));
425 : }
426 0 : return bRet;
427 : }
428 :
429 0 : }
430 :
431 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|