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