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