/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_bridges.hxx"

#include <sal/alloca.h>

#include "com/sun/star/uno/RuntimeException.hpp"

#include "rtl/ustrbuf.hxx"

#include "jni_bridge.h"


using namespace ::std;
using namespace ::rtl;

namespace
{
extern "C"
{

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_free( uno_ExtEnvironment * env, void * proxy )
    SAL_THROW_EXTERN_C();

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_acquire( uno_Interface * pUnoI )
    SAL_THROW_EXTERN_C();

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_release( uno_Interface * pUnoI )
    SAL_THROW_EXTERN_C();

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_dispatch(
    uno_Interface * pUnoI, typelib_TypeDescription const * member_td,
    void * uno_ret, void * uno_args[], uno_Any ** uno_exc )
    SAL_THROW_EXTERN_C();
}
}

namespace jni_uno
{

//______________________________________________________________________________
void Bridge::handle_java_exc(
    JNI_context const & jni,
    JLocalAutoRef const & jo_exc, uno_Any * uno_exc ) const
{
    OSL_ASSERT( jo_exc.is() );
    if (! jo_exc.is())
    {
        throw BridgeRuntimeError(
            OUSTR("java exception occured, but no java exception available!?") +
            jni.get_stack_trace() );
    }

    JLocalAutoRef jo_class( jni, jni->GetObjectClass( jo_exc.get() ) );
    JLocalAutoRef jo_class_name(
        jni, jni->CallObjectMethodA(
            jo_class.get(), m_jni_info->m_method_Class_getName, 0 ) );
    jni.ensure_no_exception();
    OUString exc_name(
        jstring_to_oustring( jni, (jstring) jo_class_name.get() ) );
    
    ::com::sun::star::uno::TypeDescription td( exc_name.pData );
    if (!td.is() || (typelib_TypeClass_EXCEPTION != td.get()->eTypeClass))
    {
        // call toString()
        JLocalAutoRef jo_descr(
            jni, jni->CallObjectMethodA(
                jo_exc.get(), m_jni_info->m_method_Object_toString, 0 ) );
        jni.ensure_no_exception();
        OUStringBuffer buf( 128 );
        buf.appendAscii(
            RTL_CONSTASCII_STRINGPARAM("non-UNO exception occurred: ") );
        buf.append( jstring_to_oustring( jni, (jstring) jo_descr.get() ) );
        buf.append( jni.get_stack_trace( jo_exc.get() ) );
        throw BridgeRuntimeError( buf.makeStringAndClear() );
    }

    auto_ptr< rtl_mem > uno_data( rtl_mem::allocate( td.get()->nSize ) );
    jvalue val;
    val.l = jo_exc.get();
    map_to_uno(
        jni, uno_data.get(), val, td.get()->pWeakRef, 0,
        false /* no assign */, false /* no out param */ );

#if OSL_DEBUG_LEVEL > 0
    // patch Message, append stack trace
    reinterpret_cast< ::com::sun::star::uno::Exception * >(
        uno_data.get() )->Message += jni.get_stack_trace( jo_exc.get() );
#endif

    typelib_typedescriptionreference_acquire( td.get()->pWeakRef );
    uno_exc->pType = td.get()->pWeakRef;
    uno_exc->pData = uno_data.release();

#if OSL_DEBUG_LEVEL > 1
    OUStringBuffer trace_buf( 128 );
    trace_buf.appendAscii(
        RTL_CONSTASCII_STRINGPARAM("exception occured uno->java: [") );
    trace_buf.append( exc_name );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("] ") );
    trace_buf.append(
        reinterpret_cast< ::com::sun::star::uno::Exception const * >(
            uno_exc->pData )->Message );
    OString cstr_trace(
        OUStringToOString(
            trace_buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
    OSL_TRACE( cstr_trace.getStr() );
#endif
}

//______________________________________________________________________________
void Bridge::call_java(
    jobject javaI, typelib_InterfaceTypeDescription * iface_td,
    sal_Int32 local_member_index, sal_Int32 function_pos_offset,
    typelib_TypeDescriptionReference * return_type,
    typelib_MethodParameter * params, sal_Int32 nParams,
    void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const
{
    OSL_ASSERT( function_pos_offset == 0 || function_pos_offset == 1 );
    
    JNI_guarded_context jni(
        m_jni_info, reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
            m_java_env->pContext ) );
    
    // assure fully initialized iface_td:
    ::com::sun::star::uno::TypeDescription iface_holder;
    if (! iface_td->aBase.bComplete) {
        iface_holder = ::com::sun::star::uno::TypeDescription(
            reinterpret_cast<typelib_TypeDescription *>(iface_td) );
        iface_holder.makeComplete();
        if (! iface_holder.get()->bComplete) {
            OUStringBuffer buf;
            buf.appendAscii(
                RTL_CONSTASCII_STRINGPARAM("cannot make type complete: ") );
            buf.append( OUString::unacquired(&iface_holder.get()->pTypeName) );
            buf.append( jni.get_stack_trace() );
            throw BridgeRuntimeError( buf.makeStringAndClear() );
        }
        iface_td = reinterpret_cast<typelib_InterfaceTypeDescription *>(
            iface_holder.get() );
        OSL_ASSERT( iface_td->aBase.eTypeClass == typelib_TypeClass_INTERFACE );
    }
    
    // prepare java args, save param td
#ifdef BROKEN_ALLOCA
    jvalue * java_args = (jvalue *) malloc( sizeof (jvalue) * nParams );
#else
    jvalue * java_args = (jvalue *) alloca( sizeof (jvalue) * nParams );
#endif
    
    sal_Int32 nPos;
    for ( nPos = 0; nPos < nParams; ++nPos )
    {
        try
        {
            typelib_MethodParameter const & param = params[ nPos ];
            java_args[ nPos ].l = 0; // if out: build up array[ 1 ]
            map_to_java(
                jni, &java_args[ nPos ],
                uno_args[ nPos ],
                param.pTypeRef, 0,
                sal_False != param.bIn /* convert uno value */,
                sal_False != param.bOut /* build up array[ 1 ] */ );
        }
        catch (...)
        {
            // cleanup
            for ( sal_Int32 n = 0; n < nPos; ++n )
            {
                typelib_MethodParameter const & param = params[ n ];
                if (param.bOut ||
                    typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass)
                {
                    jni->DeleteLocalRef( java_args[ n ].l );
                }
            }
#ifdef BROKEN_ALLOCA
	    free( java_args );
#endif
            throw;
        }
    }

    sal_Int32 base_members = iface_td->nAllMembers - iface_td->nMembers;
    OSL_ASSERT( base_members < iface_td->nAllMembers );
    sal_Int32 base_members_function_pos =
        iface_td->pMapMemberIndexToFunctionIndex[ base_members ];
    sal_Int32 member_pos = base_members + local_member_index;
    OSL_ENSURE(
        member_pos < iface_td->nAllMembers, "### member pos out of range!" );
    sal_Int32 function_pos =
        iface_td->pMapMemberIndexToFunctionIndex[ member_pos ]
        + function_pos_offset;
    OSL_ENSURE(
        function_pos >= base_members_function_pos
        && function_pos < iface_td->nMapFunctionIndexToMemberIndex,
        "### illegal function index!" );
    function_pos -= base_members_function_pos;
    
    JNI_interface_type_info const * info =
        static_cast< JNI_interface_type_info const * >(
            m_jni_info->get_type_info( jni, &iface_td->aBase ) );
    jmethodID method_id = info->m_methods[ function_pos ];

#if OSL_DEBUG_LEVEL > 1
    OUStringBuffer trace_buf( 128 );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("calling ") );
    JLocalAutoRef jo_method(
        jni, jni->ToReflectedMethod( info->m_class, method_id, JNI_FALSE ) );
    jni.ensure_no_exception();
    JLocalAutoRef jo_descr(
        jni, jni->CallObjectMethodA(
            jo_method.get(), m_jni_info->m_method_Object_toString, 0 ) );
    jni.ensure_no_exception();
    trace_buf.append( jstring_to_oustring( jni, (jstring) jo_descr.get() ) );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" on ") );
    jo_descr.reset(
        jni->CallObjectMethodA(
            javaI, m_jni_info->m_method_Object_toString, 0 ) );
    jni.ensure_no_exception();
    trace_buf.append( jstring_to_oustring( jni, (jstring) jo_descr.get() ) );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" (") );
    JLocalAutoRef jo_class( jni, jni->GetObjectClass( javaI ) );
    jo_descr.reset(
        jni->CallObjectMethodA(
            jo_class.get(), m_jni_info->m_method_Object_toString, 0 ) );
    jni.ensure_no_exception();
    trace_buf.append( jstring_to_oustring( jni, (jstring) jo_descr.get() ) );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(")") );
    OString cstr_trace(
        OUStringToOString(
            trace_buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
    OSL_TRACE( cstr_trace.getStr() );
#endif

    // complex return value
    JLocalAutoRef java_ret( jni );

    switch (return_type->eTypeClass)
    {
    case typelib_TypeClass_VOID:
        jni->CallVoidMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_CHAR:
        *(sal_Unicode *)uno_ret =
            jni->CallCharMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_BOOLEAN:
        *(sal_Bool *)uno_ret =
            jni->CallBooleanMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_BYTE:
        *(sal_Int8 *)uno_ret =
            jni->CallByteMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_SHORT:
    case typelib_TypeClass_UNSIGNED_SHORT:
        *(sal_Int16 *)uno_ret =
            jni->CallShortMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_LONG:
    case typelib_TypeClass_UNSIGNED_LONG:
        *(sal_Int32 *)uno_ret =
            jni->CallIntMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_HYPER:
    case typelib_TypeClass_UNSIGNED_HYPER:
        *(sal_Int64 *)uno_ret =
            jni->CallLongMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_FLOAT:
        *(float *)uno_ret =
            jni->CallFloatMethodA( javaI, method_id, java_args );
        break;
    case typelib_TypeClass_DOUBLE:
        *(double *)uno_ret =
            jni->CallDoubleMethodA( javaI, method_id, java_args );
        break;
    default:
        java_ret.reset(
            jni->CallObjectMethodA( javaI, method_id, java_args ) );
        break;
    }

    if (jni->ExceptionCheck())
    {
        JLocalAutoRef jo_exc( jni, jni->ExceptionOccurred() );
        jni->ExceptionClear();

        // release temp java local refs
        for ( nPos = 0; nPos < nParams; ++nPos )
        {
            typelib_MethodParameter const & param = params[ nPos ];
            if (param.bOut ||
                typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass)
            {
                jni->DeleteLocalRef( java_args[ nPos ].l );
            }
        }

        handle_java_exc( jni, jo_exc, *uno_exc );
    }
    else // no exception
    {
        for ( nPos = 0; nPos < nParams; ++nPos )
        {
            typelib_MethodParameter const & param = params[ nPos ];
            if (param.bOut)
            {
                try
                {
                    map_to_uno(
                        jni, uno_args[ nPos ],
                        java_args[ nPos ], param.pTypeRef, 0,
                        sal_False != param.bIn /* assign if inout */,
                        true /* out param */ );
                }
                catch (...)
                {
                    // cleanup uno pure out
                    for ( sal_Int32 n = 0; n < nPos; ++n )
                    {
                        typelib_MethodParameter const & p = params[ n ];
                        if (! p.bIn)
                        {
                            uno_type_destructData(
                                uno_args[ n ], p.pTypeRef, 0 );
                        }
                    }
                    // cleanup java temp local refs
                    for ( ; nPos < nParams; ++nPos )
                    {
                        typelib_MethodParameter const & p = params[ nPos ];
                        if (p.bOut ||
                            typelib_TypeClass_DOUBLE <
                              p.pTypeRef->eTypeClass)
                        {
                            jni->DeleteLocalRef( java_args[ nPos ].l );
                        }
                    }
#ifdef BROKEN_ALLOCA
		    free( java_args );
#endif
                    throw;
                }
                jni->DeleteLocalRef( java_args[ nPos ].l );
            }
            else // pure temp in param
            {
                if (typelib_TypeClass_DOUBLE < param.pTypeRef->eTypeClass)
                    jni->DeleteLocalRef( java_args[ nPos ].l );
            }
        }

        // return value
        if (typelib_TypeClass_DOUBLE < return_type->eTypeClass)
        {
            try
            {
                jvalue val;
                val.l = java_ret.get();
                map_to_uno(
                    jni, uno_ret, val, return_type, 0,
                    false /* no assign */, false /* no out param */ );
            }
            catch (...)
            {
                // cleanup uno pure out
                for ( sal_Int32 i = 0; i < nParams; ++i )
                {
                    typelib_MethodParameter const & param = params[ i ];
                    if (! param.bIn)
                    {
                        uno_type_destructData(
                            uno_args[ i ], param.pTypeRef, 0 );
                    }
                }
#ifdef BROKEN_ALLOCA
		free( java_args );
#endif
                throw;
            }
        } // else: already set integral uno return value

        // no exception occured
        *uno_exc = 0;
    }
#ifdef BROKEN_ALLOCA
    free( java_args );
#endif
}

//==== a uno proxy wrapping a java interface ===================================
struct UNO_proxy : public uno_Interface
{
    mutable oslInterlockedCount         m_ref;
    Bridge const *                      m_bridge;

    // mapping information
    jobject                             m_javaI;
    jstring                             m_jo_oid;
    OUString                            m_oid;
    JNI_interface_type_info const *     m_type_info;

    inline void acquire() const;
    inline void release() const;

    // ctor
    inline UNO_proxy(
        JNI_context const & jni, Bridge const * bridge,
        jobject javaI, jstring jo_oid, OUString const & oid,
        JNI_interface_type_info const * info );
};

//______________________________________________________________________________
inline UNO_proxy::UNO_proxy(
    JNI_context const & jni, Bridge const * bridge,
    jobject javaI, jstring jo_oid, OUString const & oid,
    JNI_interface_type_info const * info )
    : m_ref( 1 ),
      m_oid( oid ),
      m_type_info( info )
{
    JNI_info const * jni_info = bridge->m_jni_info;
    JLocalAutoRef jo_string_array(
        jni, jni->NewObjectArray( 1, jni_info->m_class_String, jo_oid ) );
    jni.ensure_no_exception();
    jvalue args[ 3 ];
    args[ 0 ].l = javaI;
    args[ 1 ].l = jo_string_array.get();
    args[ 2 ].l = info->m_type;
    jobject jo_iface = jni->CallObjectMethodA(
        jni_info->m_object_java_env,
        jni_info->m_method_IEnvironment_registerInterface, args );
    jni.ensure_no_exception();

    m_javaI = jni->NewGlobalRef( jo_iface );
    m_jo_oid = (jstring) jni->NewGlobalRef( jo_oid );
    bridge->acquire();
    m_bridge = bridge;

    // uno_Interface
    uno_Interface::acquire = UNO_proxy_acquire;
    uno_Interface::release = UNO_proxy_release;
    uno_Interface::pDispatcher = UNO_proxy_dispatch;
}

//______________________________________________________________________________
inline void UNO_proxy::acquire() const
{
    if (1 == osl_incrementInterlockedCount( &m_ref ))
    {
        // rebirth of proxy zombie
        void * that = const_cast< UNO_proxy * >( this );
        // register at uno env
        (*m_bridge->m_uno_env->registerProxyInterface)(
            m_bridge->m_uno_env, &that,
            UNO_proxy_free, m_oid.pData,
            (typelib_InterfaceTypeDescription *)m_type_info->m_td.get() );
#if OSL_DEBUG_LEVEL > 1
        OSL_ASSERT( this == (void const * const)that );
#endif
    }
}

//______________________________________________________________________________
inline void UNO_proxy::release() const
{
    if (0 == osl_decrementInterlockedCount( &m_ref ))
    {
        // revoke from uno env on last release
        (*m_bridge->m_uno_env->revokeInterface)(
            m_bridge->m_uno_env, const_cast< UNO_proxy * >( this ) );
    }
}


//______________________________________________________________________________
uno_Interface * Bridge::map_to_uno(
    JNI_context const & jni,
    jobject javaI, JNI_interface_type_info const * info ) const
{
    JLocalAutoRef jo_oid( jni, compute_oid( jni, javaI ) );
    OUString oid( jstring_to_oustring( jni, (jstring) jo_oid.get() ) );

    uno_Interface * pUnoI = 0;
    (*m_uno_env->getRegisteredInterface)(
        m_uno_env, (void **)&pUnoI,
        oid.pData, (typelib_InterfaceTypeDescription *)info->m_td.get() );

    if (0 == pUnoI) // no existing interface, register new proxy
    {
        // refcount initially 1
        pUnoI = new UNO_proxy(
            jni, const_cast< Bridge * >( this ),
            javaI, (jstring) jo_oid.get(), oid, info );
        
        (*m_uno_env->registerProxyInterface)(
            m_uno_env, (void **)&pUnoI,
            UNO_proxy_free,
            oid.pData, (typelib_InterfaceTypeDescription *)info->m_td.get() );
    }
    return pUnoI;
}

}

using namespace ::jni_uno;

namespace
{
extern "C"
{

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_free( uno_ExtEnvironment * env, void * proxy )
    SAL_THROW_EXTERN_C()
{
    UNO_proxy const * that = reinterpret_cast< UNO_proxy const * >( proxy );
    Bridge const * bridge = that->m_bridge;

    if ( env != bridge->m_uno_env ) {
        OSL_ASSERT(false);
    }
#if OSL_DEBUG_LEVEL > 1
    OString cstr_msg(
        OUStringToOString( 
            OUSTR("freeing binary uno proxy: ") + that->m_oid,
            RTL_TEXTENCODING_ASCII_US ) );
    OSL_TRACE( cstr_msg.getStr() );
#endif

    try
    {
        JNI_guarded_context jni(
            bridge->m_jni_info,
            reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
                bridge->m_java_env->pContext ) );
        
        jni->DeleteGlobalRef( that->m_javaI );
        jni->DeleteGlobalRef( that->m_jo_oid );
    }
    catch (BridgeRuntimeError & err)
    {
#if OSL_DEBUG_LEVEL > 0
        OString cstr_msg2(
            OUStringToOString( err.m_message, RTL_TEXTENCODING_ASCII_US ) );
        OSL_ENSURE( 0, cstr_msg2.getStr() );
#else
        (void) err; // unused
#endif
    }
    catch (::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
    {
        OSL_ENSURE(
            0,
            "[jni_uno bridge error] attaching current thread to java failed!" );
    }

    bridge->release();
#if OSL_DEBUG_LEVEL > 1
    *(int *)that = 0xdeadcafe;
#endif
    delete that;
}

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_acquire( uno_Interface * pUnoI )
    SAL_THROW_EXTERN_C()
{
    UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI );
    that->acquire();
}

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_release( uno_Interface * pUnoI )
    SAL_THROW_EXTERN_C()
{
    UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI );
    that->release();
}

//------------------------------------------------------------------------------
void SAL_CALL UNO_proxy_dispatch(
    uno_Interface * pUnoI, typelib_TypeDescription const * member_td,
    void * uno_ret, void * uno_args [], uno_Any ** uno_exc )
    SAL_THROW_EXTERN_C()
{
    UNO_proxy const * that = static_cast< UNO_proxy const * >( pUnoI );
    Bridge const * bridge = that->m_bridge;

#if OSL_DEBUG_LEVEL > 1
    OUStringBuffer trace_buf( 64 );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("uno->java call: ") );
    trace_buf.append( OUString::unacquired( &member_td->pTypeName ) );
    trace_buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" on oid ") );
    trace_buf.append( that->m_oid );
    OString cstr_msg(
        OUStringToOString(
            trace_buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
    OSL_TRACE( cstr_msg.getStr() );
#endif

    try
    {
        switch (member_td->eTypeClass)
        {
        case typelib_TypeClass_INTERFACE_ATTRIBUTE:
        {
            typelib_InterfaceAttributeTypeDescription const * attrib_td =
                reinterpret_cast<
                typelib_InterfaceAttributeTypeDescription const * >(
                    member_td );
            com::sun::star::uno::TypeDescription attrib_holder;
            while ( attrib_td->pBaseRef != 0 ) {
                attrib_holder = com::sun::star::uno::TypeDescription(
                    attrib_td->pBaseRef );
                OSL_ASSERT(
                    attrib_holder.get()->eTypeClass
                    == typelib_TypeClass_INTERFACE_ATTRIBUTE );
                attrib_td = reinterpret_cast<
                    typelib_InterfaceAttributeTypeDescription * >(
                        attrib_holder.get() );
            }
            typelib_InterfaceTypeDescription * iface_td = attrib_td->pInterface;

            if (0 == uno_ret) // is setter method
            {
                typelib_MethodParameter param;
                param.pTypeRef = attrib_td->pAttributeTypeRef;
                param.bIn = sal_True;
                param.bOut = sal_False;

                bridge->call_java(
                    that->m_javaI, iface_td,
                    attrib_td->nIndex, 1, // get, then set method
                    bridge->m_jni_info->m_void_type.getTypeLibType(),
                    &param, 1,
                    0, uno_args, uno_exc );
            }
            else // is getter method
            {
                bridge->call_java(
                    that->m_javaI, iface_td, attrib_td->nIndex, 0,
                    attrib_td->pAttributeTypeRef,
                    0, 0, // no params
                    uno_ret, 0, uno_exc );
            }
            break;
        }
        case typelib_TypeClass_INTERFACE_METHOD:
        {
            typelib_InterfaceMethodTypeDescription const * method_td =
                reinterpret_cast<
                typelib_InterfaceMethodTypeDescription const * >(
                    member_td );
            com::sun::star::uno::TypeDescription method_holder;
            while ( method_td->pBaseRef != 0 ) {
                method_holder = com::sun::star::uno::TypeDescription(
                    method_td->pBaseRef );
                OSL_ASSERT(
                    method_holder.get()->eTypeClass
                    == typelib_TypeClass_INTERFACE_METHOD );
                method_td = reinterpret_cast<
                    typelib_InterfaceMethodTypeDescription * >(
                        method_holder.get() );
            }
            typelib_InterfaceTypeDescription * iface_td = method_td->pInterface;

            switch ( method_td->aBase.nPosition )
            {
            case 0: // queryInterface()
            {
                TypeDescr demanded_td(
                    *reinterpret_cast< typelib_TypeDescriptionReference ** >(
                        uno_args[ 0 ] ) );
                if (typelib_TypeClass_INTERFACE !=
                      demanded_td.get()->eTypeClass)
                {
                    throw BridgeRuntimeError(
                        OUSTR("queryInterface() call demands "
                              "an INTERFACE type!") );
                }

                uno_Interface * pInterface = 0;
                (*bridge->m_uno_env->getRegisteredInterface)(
                    bridge->m_uno_env,
                    (void **) &pInterface, that->m_oid.pData,
                    (typelib_InterfaceTypeDescription *)demanded_td.get() );

                if (0 == pInterface)
                {
                    JNI_info const * jni_info = bridge->m_jni_info;
                    JNI_guarded_context jni(
                        jni_info,
                        reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
                            bridge->m_java_env->pContext ) );

                    JNI_interface_type_info const * info =
                        static_cast< JNI_interface_type_info const * >(
                            jni_info->get_type_info( jni, demanded_td.get() ) );

                    jvalue args[ 2 ];
                    args[ 0 ].l = info->m_type;
                    args[ 1 ].l = that->m_javaI;

                    JLocalAutoRef jo_ret(
                        jni, jni->CallStaticObjectMethodA(
                            jni_info->m_class_UnoRuntime,
                            jni_info->m_method_UnoRuntime_queryInterface,
                            args ) );
                    
                    if (jni->ExceptionCheck())
                    {
                        JLocalAutoRef jo_exc( jni, jni->ExceptionOccurred() );
                        jni->ExceptionClear();
                        bridge->handle_java_exc( jni, jo_exc, *uno_exc );
                    }
                    else
                    {
                        if (jo_ret.is())
                        {
#if OSL_DEBUG_LEVEL > 0
                            JLocalAutoRef jo_oid(
                                jni, compute_oid( jni, jo_ret.get() ) );
                            OUString oid( jstring_to_oustring(
                                              jni, (jstring) jo_oid.get() ) );
                            OSL_ENSURE(
                                oid.equals( that->m_oid ),
                                "### different oids!" );
#endif
                            // refcount initially 1
                            uno_Interface * pUnoI2 = new UNO_proxy(
                                jni, bridge, jo_ret.get(),
                                that->m_jo_oid, that->m_oid, info );

                            (*bridge->m_uno_env->registerProxyInterface)(
                                bridge->m_uno_env,
                                (void **) &pUnoI2,
                                UNO_proxy_free, that->m_oid.pData,
                                reinterpret_cast<
                                  typelib_InterfaceTypeDescription * >(
                                      info->m_td.get() ) );
                            
                            uno_any_construct(
                                (uno_Any *)uno_ret, &pUnoI2,
                                demanded_td.get(), 0 );
                            (*pUnoI2->release)( pUnoI2 );
                        }
                        else // object does not support demanded interface
                        {
                            uno_any_construct(
                                reinterpret_cast< uno_Any * >( uno_ret ),
                                0, 0, 0 );
                        }
                        // no exception occured
                        *uno_exc = 0;
                    }
                }
                else
                {
                    uno_any_construct(
                        reinterpret_cast< uno_Any * >( uno_ret ),
                        &pInterface, demanded_td.get(), 0 );
                    (*pInterface->release)( pInterface );
                    *uno_exc = 0;
                }
                break;
            }
            case 1: // acquire this proxy
                that->acquire();
                *uno_exc = 0;
                break;
            case 2: // release this proxy
                that->release();
                *uno_exc = 0;
                break;
            default: // arbitrary method call
                bridge->call_java(
                    that->m_javaI, iface_td, method_td->nIndex, 0,
                    method_td->pReturnTypeRef,
                    method_td->pParams, method_td->nParams,
                    uno_ret, uno_args, uno_exc );
                break;
            }
            break;
        }
        default:
        {
            throw BridgeRuntimeError(
                OUSTR("illegal member type description!") );
        }
        }
    }
    catch (BridgeRuntimeError & err)
    {
        OUStringBuffer buf( 128 );
        buf.appendAscii(
            RTL_CONSTASCII_STRINGPARAM(
                "[jni_uno bridge error] UNO calling Java method ") );
        if (typelib_TypeClass_INTERFACE_METHOD == member_td->eTypeClass ||
            typelib_TypeClass_INTERFACE_ATTRIBUTE == member_td->eTypeClass)
        {
            buf.append( OUString::unacquired(
                            &reinterpret_cast<
                            typelib_InterfaceMemberTypeDescription const * >(
                                member_td )->pMemberName ) );
        }
        buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(": ") );
        buf.append( err.m_message );
        // binary identical struct
        ::com::sun::star::uno::RuntimeException exc(
            buf.makeStringAndClear(),
            ::com::sun::star::uno::Reference<
              ::com::sun::star::uno::XInterface >() );
        ::com::sun::star::uno::Type const & exc_type = ::getCppuType( &exc );
        uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), 0 );
#if OSL_DEBUG_LEVEL > 0
        OString cstr_msg2(
            OUStringToOString( exc.Message, RTL_TEXTENCODING_ASCII_US ) );
        OSL_TRACE( "%s", cstr_msg2.getStr() );
#endif
    }
    catch (::jvmaccess::VirtualMachine::AttachGuard::CreationException &)
    {
        // binary identical struct
        ::com::sun::star::uno::RuntimeException exc(
            OUSTR("[jni_uno bridge error] attaching current thread "
                  "to java failed!"),
            ::com::sun::star::uno::Reference<
              ::com::sun::star::uno::XInterface >() );
        ::com::sun::star::uno::Type const & exc_type = ::getCppuType( &exc );
        uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), 0 );
#if OSL_DEBUG_LEVEL > 0
        OString cstr_msg2(
            OUStringToOString( exc.Message, RTL_TEXTENCODING_ASCII_US ) );
        OSL_ENSURE( 0, cstr_msg2.getStr() );
#endif
    }
}

}
}
