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 :
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;
46 :
47 : #define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
48 :
49 : namespace pyuno
50 : {
51 :
52 0 : Adapter::Adapter( const PyRef & ref, const Sequence< Type > &types )
53 : : mWrappedObject( ref ),
54 0 : mInterpreter( (PyThreadState_Get()->interp) ),
55 0 : mTypes( types )
56 0 : {}
57 :
58 0 : Adapter::~Adapter()
59 : {
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 0 : decreaseRefCount( mInterpreter, mWrappedObject.get() );
63 0 : mWrappedObject.scratch();
64 0 : }
65 :
66 0 : static cppu::OImplementationId g_id( false );
67 :
68 0 : Sequence<sal_Int8> Adapter::getUnoTunnelImplementationId()
69 : {
70 0 : return g_id.getImplementationId();
71 : }
72 :
73 0 : sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id) throw (RuntimeException, std::exception)
74 : {
75 0 : if( id == g_id.getImplementationId() )
76 0 : return reinterpret_cast<sal_Int64>(this);
77 0 : return 0;
78 : }
79 :
80 0 : void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime )
81 : throw ( InvocationTargetException )
82 : {
83 0 : if( PyErr_Occurred() )
84 : {
85 0 : PyRef excType, excValue, excTraceback;
86 0 : PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
87 0 : Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
88 : throw InvocationTargetException(
89 0 : ((com::sun::star::uno::Exception*)unoExc.getValue())->Message,
90 0 : Reference<XInterface>(), unoExc );
91 : }
92 0 : }
93 :
94 0 : Reference< XIntrospectionAccess > Adapter::getIntrospection()
95 : throw ( RuntimeException, std::exception )
96 : {
97 : // not supported
98 0 : return Reference< XIntrospectionAccess > ();
99 : }
100 :
101 0 : Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
102 : {
103 0 : Sequence< sal_Int16 > ret;
104 0 : MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
105 0 : if( ii == m_methodOutIndexMap.end() )
106 : {
107 :
108 0 : Runtime runtime;
109 : {
110 0 : PyThreadDetach antiguard;
111 :
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 0 : runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
116 :
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 0 : runtime.getImpl()->cargo->xIntrospection->inspect( makeAny( unoAdapterObject ) );
122 :
123 0 : if( !introspection.is() )
124 : {
125 : throw RuntimeException(
126 : OUString( "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" ),
127 0 : Reference< XInterface > () );
128 : }
129 :
130 0 : Reference< XIdlMethod > method = introspection->getMethod(
131 0 : functionName, com::sun::star::beans::MethodConcept::ALL );
132 0 : if( ! method.is( ) )
133 : {
134 : throw RuntimeException(
135 0 : "pyuno bridge: Couldn't get reflection for method " + functionName,
136 0 : Reference< XInterface > () );
137 : }
138 :
139 0 : Sequence< ParamInfo > seqInfo = method->getParameterInfos();
140 : int i;
141 0 : int nOuts = 0;
142 0 : for( i = 0 ; i < seqInfo.getLength() ; i ++ )
143 : {
144 0 : if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
145 0 : seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
146 : {
147 : // sequence must be interpreted as return value/outparameter tuple !
148 0 : nOuts ++;
149 : }
150 : }
151 :
152 0 : if( nOuts )
153 : {
154 0 : ret.realloc( nOuts );
155 0 : sal_Int32 nOutsAssigned = 0;
156 0 : for( i = 0 ; i < seqInfo.getLength() ; i ++ )
157 : {
158 0 : if( seqInfo[i].aMode == com::sun::star::reflection::ParamMode_OUT ||
159 0 : seqInfo[i].aMode == com::sun::star::reflection::ParamMode_INOUT )
160 : {
161 0 : ret[nOutsAssigned] = (sal_Int16) i;
162 0 : nOutsAssigned ++;
163 : }
164 : }
165 0 : }
166 : }
167 : // guard active again !
168 0 : m_methodOutIndexMap[ functionName ] = ret;
169 : }
170 : else
171 : {
172 0 : ret = ii->second;
173 : }
174 0 : return ret;
175 : }
176 :
177 0 : 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, std::exception)
182 : {
183 0 : Any ret;
184 :
185 : // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
186 : // always handled by the adapter directly.
187 0 : if( aParams.getLength() == 1 && aFunctionName.equalsAscii( "getSomething" ) )
188 : {
189 0 : Sequence< sal_Int8 > id;
190 0 : if( aParams[0] >>= id )
191 0 : return com::sun::star::uno::makeAny( getSomething( id ) );
192 :
193 : }
194 :
195 0 : RuntimeCargo *cargo = 0;
196 : try
197 : {
198 0 : PyThreadAttach guard( mInterpreter );
199 : {
200 : // convert parameters to python args
201 : // TODO: Out parameter
202 0 : Runtime runtime;
203 0 : cargo = runtime.getImpl()->cargo;
204 0 : if( isLog( cargo, LogLevel::CALL ) )
205 : {
206 : logCall( cargo, "try uno->py[0x",
207 0 : mWrappedObject.get(), aFunctionName, aParams );
208 : }
209 :
210 0 : sal_Int32 size = aParams.getLength();
211 0 : PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE );
212 : int i;
213 : // fill tuple with default values in case of exceptions
214 0 : for( i = 0 ;i < size ; i ++ )
215 : {
216 0 : Py_INCREF( Py_None );
217 0 : PyTuple_SetItem( argsTuple.get(), i, Py_None );
218 : }
219 :
220 : // convert args to python
221 0 : for( i = 0; i < size ; i ++ )
222 : {
223 0 : PyRef val = runtime.any2PyObject( aParams[i] );
224 0 : PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
225 0 : }
226 :
227 : // get callable
228 0 : PyRef method(PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aFunctionName)),
229 0 : SAL_NO_ACQUIRE);
230 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
231 0 : if( !method.is() )
232 : {
233 0 : OUStringBuffer buf;
234 0 : buf.appendAscii( "pyuno::Adapater: Method " ).append( aFunctionName );
235 0 : buf.appendAscii( " is not implemented at object " );
236 0 : PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
237 0 : buf.append(pyString2ustring(str.get()));
238 0 : throw IllegalArgumentException( buf.makeStringAndClear(), Reference< XInterface > (),0 );
239 : }
240 :
241 0 : PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
242 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
243 0 : if( pyRet.is() )
244 : {
245 0 : ret = runtime.pyObject2Any( pyRet );
246 :
247 0 : if( ret.hasValue() &&
248 0 : ret.getValueTypeClass() == com::sun::star::uno::TypeClass_SEQUENCE &&
249 0 : ! aFunctionName.equalsAscii( "getTypes" ) && // needed by introspection itself !
250 0 : ! aFunctionName.equalsAscii( "getImplementationId" ) ) // needed by introspection itself !
251 : {
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 0 : aOutParamIndex = getOutIndexes( aFunctionName );
259 0 : if( aOutParamIndex.getLength() )
260 : {
261 : // out parameters exist, extract the sequence
262 0 : Sequence< Any > seq;
263 0 : if( ! ( ret >>= seq ) )
264 : {
265 : throw RuntimeException(
266 0 : "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName,
267 0 : Reference< XInterface > () );
268 : }
269 :
270 0 : if( aOutParamIndex.getLength() +1 != seq.getLength() )
271 : {
272 0 : OUStringBuffer buf;
273 0 : buf.append( "pyuno bridge: expected for method " );
274 0 : buf.append( aFunctionName );
275 0 : buf.append( " one return value and " );
276 0 : buf.append( (sal_Int32) aOutParamIndex.getLength() );
277 0 : buf.append( " out parameters, got a sequence of " );
278 0 : buf.append( seq.getLength() );
279 0 : buf.append( " elements as return value." );
280 0 : throw RuntimeException(buf.makeStringAndClear(), *this );
281 : }
282 :
283 0 : aOutParam.realloc( aOutParamIndex.getLength() );
284 0 : ret = seq[0];
285 0 : for( i = 0 ; i < aOutParamIndex.getLength() ; i ++ )
286 : {
287 0 : aOutParam[i] = seq[1+i];
288 0 : }
289 : }
290 : // else { sequence is a return value !}
291 : }
292 : }
293 :
294 : // log the reply, if desired
295 0 : if( isLog( cargo, LogLevel::CALL ) )
296 : {
297 : logReply( cargo, "success uno->py[0x" ,
298 0 : mWrappedObject.get(), aFunctionName, ret, aOutParam );
299 0 : }
300 0 : }
301 :
302 : }
303 0 : catch( const InvocationTargetException & e )
304 : {
305 0 : if( isLog( cargo, LogLevel::CALL ) )
306 : {
307 : logException(
308 : cargo, "except uno->py[0x" ,
309 0 : mWrappedObject.get(), aFunctionName,
310 0 : e.TargetException.getValue(),e.TargetException.getValueType() );
311 : }
312 0 : throw;
313 : }
314 0 : catch( const IllegalArgumentException & e )
315 : {
316 0 : if( isLog( cargo, LogLevel::CALL ) )
317 : {
318 : logException(
319 : cargo, "except uno->py[0x" ,
320 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
321 : }
322 0 : throw;
323 : }
324 0 : catch( const RuntimeException & e )
325 : {
326 0 : if( cargo && isLog( cargo, LogLevel::CALL ) )
327 : {
328 : logException(
329 : cargo, "except uno->py[0x" ,
330 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
331 : }
332 0 : throw;
333 : }
334 0 : catch( const CannotConvertException & e )
335 : {
336 0 : if( isLog( cargo, LogLevel::CALL ) )
337 : {
338 : logException(
339 : cargo, "except uno->py[0x" ,
340 0 : mWrappedObject.get(), aFunctionName, &e,getCppuType(&e) );
341 : }
342 0 : throw;
343 : }
344 0 : return ret;
345 : }
346 :
347 0 : void Adapter::setValue( const OUString & aPropertyName, const Any & value )
348 : throw( UnknownPropertyException, CannotConvertException, InvocationTargetException,RuntimeException, std::exception)
349 : {
350 0 : if( !hasProperty( aPropertyName ) )
351 : {
352 0 : OUStringBuffer buf;
353 0 : buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
354 0 : buf.appendAscii( " is unknown." );
355 0 : throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
356 : }
357 :
358 0 : PyThreadAttach guard( mInterpreter );
359 : try
360 : {
361 0 : Runtime runtime;
362 0 : PyRef obj = runtime.any2PyObject( value );
363 :
364 : PyObject_SetAttrString(
365 0 : mWrappedObject.get(), (char*)TO_ASCII(aPropertyName), obj.get() );
366 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
367 :
368 : }
369 0 : catch( const IllegalArgumentException & exc )
370 : {
371 0 : throw InvocationTargetException( exc.Message, *this, com::sun::star::uno::makeAny( exc ) );
372 0 : }
373 0 : }
374 :
375 0 : Any Adapter::getValue( const OUString & aPropertyName )
376 : throw ( UnknownPropertyException, RuntimeException, std::exception )
377 : {
378 0 : Any ret;
379 0 : PyThreadAttach guard( mInterpreter );
380 : {
381 0 : Runtime runtime;
382 : PyRef pyRef(
383 0 : PyObject_GetAttrString( mWrappedObject.get(), (char*)TO_ASCII(aPropertyName) ),
384 0 : SAL_NO_ACQUIRE );
385 :
386 0 : raiseInvocationTargetExceptionWhenNeeded( runtime);
387 0 : if( !pyRef.is() )
388 : {
389 0 : OUStringBuffer buf;
390 0 : buf.appendAscii( "pyuno::Adapater: Property " ).append( aPropertyName );
391 0 : buf.appendAscii( " is unknown." );
392 0 : throw UnknownPropertyException( buf.makeStringAndClear(), Reference< XInterface > () );
393 : }
394 0 : ret = runtime.pyObject2Any( pyRef );
395 : }
396 0 : return ret;
397 : }
398 :
399 0 : sal_Bool Adapter::hasMethod( const OUString & aMethodName )
400 : throw ( RuntimeException, std::exception )
401 : {
402 0 : return hasProperty( aMethodName );
403 : }
404 :
405 0 : sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
406 : throw ( RuntimeException, std::exception )
407 : {
408 0 : bool bRet = false;
409 0 : PyThreadAttach guard( mInterpreter );
410 : {
411 : bRet = PyObject_HasAttrString(
412 0 : mWrappedObject.get() , (char*) TO_ASCII( aPropertyName ));
413 : }
414 0 : return bRet;
415 : }
416 :
417 0 : }
418 :
419 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|