1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
22 #include "IdentityMapping.hxx"
24 #include <boost/unordered_map.hpp>
29 #include "rtl/ustring.hxx"
30 #include "rtl/ustrbuf.hxx"
31 #include "osl/module.h"
32 #include "osl/diagnose.h"
33 #include "osl/mutex.hxx"
34 #include "osl/interlck.h"
36 #include "uno/dispatcher.h"
37 #include "uno/mapping.h"
38 #include "uno/lbnames.h"
39 #include "uno/environment.hxx"
41 #include "typelib/typedescription.h"
43 #include "cppu/EnvDcp.hxx"
44 #include "cascade_mapping.hxx"
45 #include "loadmodule.hxx"
49 using namespace com::sun::star::uno
;
50 using ::rtl::OUString
;
51 using ::rtl::OUStringBuffer
;
52 using ::rtl::OUStringToOString
;
60 uno_Mapping
* _pMapping
;
63 inline explicit Mapping( uno_Mapping
* pMapping
= 0 ) SAL_THROW(());
64 inline Mapping( const Mapping
& rMapping
) SAL_THROW(());
65 inline ~Mapping() SAL_THROW(());
66 inline Mapping
& SAL_CALL
operator = ( uno_Mapping
* pMapping
) SAL_THROW(());
67 inline Mapping
& SAL_CALL
operator = ( const Mapping
& rMapping
) SAL_THROW(())
68 { return operator = ( rMapping
._pMapping
); }
69 inline uno_Mapping
* SAL_CALL
get() const SAL_THROW(())
71 inline sal_Bool SAL_CALL
is() const SAL_THROW(())
72 { return (_pMapping
!= 0); }
74 //__________________________________________________________________________________________________
75 inline Mapping::Mapping( uno_Mapping
* pMapping
) SAL_THROW(())
76 : _pMapping( pMapping
)
79 (*_pMapping
->acquire
)( _pMapping
);
81 //__________________________________________________________________________________________________
82 inline Mapping::Mapping( const Mapping
& rMapping
) SAL_THROW(())
83 : _pMapping( rMapping
._pMapping
)
86 (*_pMapping
->acquire
)( _pMapping
);
88 //__________________________________________________________________________________________________
89 inline Mapping::~Mapping() SAL_THROW(())
92 (*_pMapping
->release
)( _pMapping
);
94 //__________________________________________________________________________________________________
95 inline Mapping
& Mapping::operator = ( uno_Mapping
* pMapping
) SAL_THROW(())
98 (*pMapping
->acquire
)( pMapping
);
100 (*_pMapping
->release
)( _pMapping
);
101 _pMapping
= pMapping
;
105 //==================================================================================================
109 uno_Mapping
* pMapping
;
110 uno_freeMappingFunc freeMapping
;
111 OUString aMappingName
;
114 uno_Mapping
* pMapping_
, uno_freeMappingFunc freeMapping_
,
115 const OUString
& rMappingName_
)
118 , pMapping( pMapping_
)
119 , freeMapping( freeMapping_
)
120 , aMappingName( rMappingName_
)
123 //--------------------------------------------------------------------------------------------------
124 struct FctOUStringHash
: public std::unary_function
< const OUString
&, size_t >
126 size_t operator()( const OUString
& rKey
) const SAL_THROW(())
127 { return (size_t)rKey
.hashCode(); }
129 //--------------------------------------------------------------------------------------------------
130 struct FctPtrHash
: public std::unary_function
< uno_Mapping
*, size_t >
132 size_t operator()( uno_Mapping
* pKey
) const SAL_THROW(())
133 { return (size_t)pKey
; }
136 typedef boost::unordered_map
<
137 OUString
, MappingEntry
*, FctOUStringHash
, equal_to
< OUString
> > t_OUString2Entry
;
138 typedef boost::unordered_map
<
139 uno_Mapping
*, MappingEntry
*, FctPtrHash
, equal_to
< uno_Mapping
* > > t_Mapping2Entry
;
141 typedef set
< uno_getMappingFunc
> t_CallbackSet
;
142 typedef set
< OUString
> t_OUStringSet
;
144 //==================================================================================================
147 Mutex aMappingsMutex
;
148 t_OUString2Entry aName2Entry
;
149 t_Mapping2Entry aMapping2Entry
;
151 Mutex aCallbacksMutex
;
152 t_CallbackSet aCallbacks
;
154 Mutex aNegativeLibsMutex
;
155 t_OUStringSet aNegativeLibs
;
157 //--------------------------------------------------------------------------------------------------
158 static MappingsData
& getMappingsData() SAL_THROW(())
160 static MappingsData
* s_p
= 0;
163 MutexGuard
aGuard( Mutex::getGlobalMutex() );
166 //TODO This memory is leaked; see #i63473# for when this should be
168 s_p
= new MappingsData
;
175 * This class mediates two different mapping via uno, e.g. form any language to uno,
176 * then from uno to any other language.
178 struct uno_Mediate_Mapping
: public uno_Mapping
188 OUString aAddPurpose
;
191 const Environment
& rFrom_
, const Environment
& rTo_
,
192 const Mapping
& rFrom2Uno_
, const Mapping
& rUno2To_
,
193 const OUString
& rAddPurpose
)
198 //--------------------------------------------------------------------------------------------------
199 static void SAL_CALL
mediate_free( uno_Mapping
* pMapping
)
202 delete static_cast< uno_Mediate_Mapping
* >( pMapping
);
204 //--------------------------------------------------------------------------------------------------
205 static void SAL_CALL
mediate_acquire( uno_Mapping
* pMapping
)
208 if (1 == osl_atomic_increment(
209 & static_cast< uno_Mediate_Mapping
* >( pMapping
)->nRef
))
212 &pMapping
, mediate_free
,
213 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aFrom
.get(),
214 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aTo
.get(),
215 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aAddPurpose
.pData
);
218 //--------------------------------------------------------------------------------------------------
219 static void SAL_CALL
mediate_release( uno_Mapping
* pMapping
)
222 if (! osl_atomic_decrement(
223 & static_cast< uno_Mediate_Mapping
* >( pMapping
)->nRef
))
225 uno_revokeMapping( pMapping
);
228 //--------------------------------------------------------------------------------------------------
229 static void SAL_CALL
mediate_mapInterface(
230 uno_Mapping
* pMapping
,
231 void ** ppOut
, void * pInterface
,
232 typelib_InterfaceTypeDescription
* pInterfaceTypeDescr
)
235 OSL_ENSURE( pMapping
&& ppOut
, "### null ptr!" );
236 if (pMapping
&& ppOut
)
238 uno_Mediate_Mapping
* that
= static_cast< uno_Mediate_Mapping
* >( pMapping
);
239 uno_Mapping
* pFrom2Uno
= that
->aFrom2Uno
.get();
241 uno_Interface
* pUnoI
= 0;
242 (*pFrom2Uno
->mapInterface
)( pFrom2Uno
, (void **) &pUnoI
, pInterface
, pInterfaceTypeDescr
);
245 void * pOut
= *ppOut
;
248 uno_ExtEnvironment
* pTo
= that
->aTo
.get()->pExtEnv
;
249 OSL_ENSURE( 0 != pTo
, "### cannot release out interface: leaking!" );
251 (*pTo
->releaseInterface
)( pTo
, pOut
);
252 *ppOut
= 0; // set to 0 anyway, because mapping was not successful!
257 uno_Mapping
* pUno2To
= that
->aUno2To
.get();
258 (*pUno2To
->mapInterface
)( pUno2To
, ppOut
, pUnoI
, pInterfaceTypeDescr
);
259 (*pUnoI
->release
)( pUnoI
);
264 //__________________________________________________________________________________________________
265 uno_Mediate_Mapping::uno_Mediate_Mapping(
266 const Environment
& rFrom_
, const Environment
& rTo_
,
267 const Mapping
& rFrom2Uno_
, const Mapping
& rUno2To_
,
268 const OUString
& rAddPurpose_
)
273 , aFrom2Uno( rFrom2Uno_
)
274 , aUno2To( rUno2To_
)
275 , aAddPurpose( rAddPurpose_
)
277 uno_Mapping::acquire
= mediate_acquire
;
278 uno_Mapping::release
= mediate_release
;
279 uno_Mapping::mapInterface
= mediate_mapInterface
;
282 //==================================================================================================
283 static inline OUString
getMappingName(
284 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
287 OUStringBuffer
aKey( 64 );
288 aKey
.append( rAddPurpose
);
289 aKey
.append( (sal_Unicode
)';' );
290 aKey
.append( rFrom
.getTypeName() );
291 aKey
.append( (sal_Unicode
)'[' );
292 aKey
.append( reinterpret_cast< sal_IntPtr
>(rFrom
.get()), 16 );
294 aKey
.append( rTo
.getTypeName() );
295 aKey
.append( (sal_Unicode
)'[' );
296 aKey
.append( reinterpret_cast< sal_IntPtr
>(rTo
.get()), 16 );
297 aKey
.append( (sal_Unicode
)']' );
298 return aKey
.makeStringAndClear();
300 //==================================================================================================
301 static inline OUString
getBridgeName(
302 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
305 OUStringBuffer
aBridgeName( 16 );
306 if (!rAddPurpose
.isEmpty())
308 aBridgeName
.append( rAddPurpose
);
309 aBridgeName
.append( (sal_Unicode
)'_' );
311 aBridgeName
.append( EnvDcp::getTypeName(rFrom
.getTypeName()) );
312 aBridgeName
.append( (sal_Unicode
)'_' );
313 aBridgeName
.append( EnvDcp::getTypeName(rTo
.getTypeName()) );
314 return aBridgeName
.makeStringAndClear();
316 //==================================================================================================
317 static inline void setNegativeBridge( const OUString
& rBridgeName
)
320 MappingsData
& rData
= getMappingsData();
321 MutexGuard
aGuard( rData
.aNegativeLibsMutex
);
322 rData
.aNegativeLibs
.insert( rBridgeName
);
325 #ifdef DISABLE_DYNLOADING
327 static uno_ext_getMappingFunc
selectMapFunc( const OUString
& rBridgeName
)
330 if (rBridgeName
.equalsAscii( CPPU_CURRENT_LANGUAGE_BINDING_NAME
"_uno" ))
331 return CPPU_ENV_uno_ext_getMapping
;
332 #if HAVE_FEATURE_JAVA
333 if (rBridgeName
.equalsAscii( "java" "_uno" ))
334 return java_uno_ext_getMapping
;
338 // I don't think the affine or log bridges will be needed on any
339 // DISABLE_DYNLOADING platform (iOS at least, possibly Android), but if
340 // somebody wants to experiment, need to find out then whether these are
342 if (rBridgeName
.equalsAscii( "affine_uno_uno" ))
343 return affine_uno_uno_ext_getMapping
;
344 if (rBridgeName
.equalsAscii( "log_uno_uno" ))
345 return log_uno_uno_ext_getMapping
;
352 static inline oslModule
loadModule( const OUString
& rBridgeName
)
357 MappingsData
& rData
= getMappingsData();
358 MutexGuard
aGuard( rData
.aNegativeLibsMutex
);
359 const t_OUStringSet::const_iterator
iFind( rData
.aNegativeLibs
.find( rBridgeName
) );
360 bNeg
= (iFind
!= rData
.aNegativeLibs
.end());
365 oslModule hModule
= cppu::detail::loadModule( rBridgeName
);
370 setNegativeBridge( rBridgeName
); // no load again
377 //==================================================================================================
378 static Mapping
loadExternalMapping(
379 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
382 OSL_ASSERT( rFrom
.is() && rTo
.is() );
383 if (rFrom
.is() && rTo
.is())
385 #ifdef DISABLE_DYNLOADING
387 uno_ext_getMappingFunc fpGetMapFunc
= 0;
389 if (EnvDcp::getTypeName(rFrom
.getTypeName()).startsWith( UNO_LB_UNO
))
391 aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
);
392 fpGetMapFunc
= selectMapFunc( aName
);
396 aName
= getBridgeName( rFrom
, rTo
, rAddPurpose
);
397 fpGetMapFunc
= selectMapFunc( aName
);
401 aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
);
402 fpGetMapFunc
= selectMapFunc( aName
);
407 #if OSL_DEBUG_LEVEL > 1
408 OSL_TRACE( "Could not find mapfunc for %s", OUStringToOString( aName
, RTL_TEXTENCODING_ASCII_US
).getStr());
416 (*fpGetMapFunc
)( (uno_Mapping
**)&aExt
, rFrom
.get(), rTo
.get() );
417 OSL_ASSERT( aExt
.is() );
423 oslModule hModule
= 0;
426 if ( EnvDcp::getTypeName(rFrom
.getTypeName()) == UNO_LB_UNO
)
427 hModule
= loadModule( aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
) );
429 hModule
= loadModule( aName
= getBridgeName( rFrom
, rTo
, rAddPurpose
) );
431 hModule
= loadModule( aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
) );
435 OUString
aSymbolName( UNO_EXT_GETMAPPING
);
436 uno_ext_getMappingFunc fpGetMapFunc
=
437 (uno_ext_getMappingFunc
)::osl_getFunctionSymbol(
438 hModule
, aSymbolName
.pData
);
443 (*fpGetMapFunc
)( (uno_Mapping
**)&aExt
, rFrom
.get(), rTo
.get() );
444 OSL_ASSERT( aExt
.is() );
450 ::osl_unloadModule( hModule
);
451 setNegativeBridge( aName
);
458 //==================================================================================================
459 static Mapping
getDirectMapping(
460 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
= OUString() )
463 OSL_ASSERT( rFrom
.is() && rTo
.is() );
464 if (rFrom
.is() && rTo
.is())
466 MappingsData
& rData
= getMappingsData();
467 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
469 // try to find registered mapping
470 const t_OUString2Entry::const_iterator
iFind( rData
.aName2Entry
.find(
471 getMappingName( rFrom
, rTo
, rAddPurpose
) ) );
473 if (iFind
== rData
.aName2Entry
.end())
476 return loadExternalMapping( rFrom
, rTo
, rAddPurpose
);
480 return Mapping( (*iFind
).second
->pMapping
);
486 //--------------------------------------------------------------------------------------------------
487 static inline Mapping
createMediateMapping(
488 const Environment
& rFrom
, const Environment
& rTo
,
489 const Mapping
& rFrom2Uno
, const Mapping
& rUno2To
,
490 const OUString
& rAddPurpose
)
493 uno_Mapping
* pRet
= new uno_Mediate_Mapping(
494 rFrom
, rTo
, rFrom2Uno
, rUno2To
, rAddPurpose
); // ref count initially 1
496 &pRet
, mediate_free
, rFrom
.get(), rTo
.get(), rAddPurpose
.pData
);
497 Mapping
aRet( pRet
);
498 (*pRet
->release
)( pRet
);
501 //==================================================================================================
502 static Mapping
getMediateMapping(
503 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
509 // backwards: from dest to source of mapping chain
512 OUString
aUnoEnvTypeName( UNO_LB_UNO
);
513 if (rTo
.getTypeName() == aUnoEnvTypeName
) // to is uno
516 // no Uno2To mapping necessary
520 // get registered uno env
521 ::uno_getEnvironment( (uno_Environment
**)&aUno
, aUnoEnvTypeName
.pData
, 0 );
523 aUno2To
= getDirectMapping( aUno
, rTo
);
530 if (!rAddPurpose
.isEmpty()) // insert purpose mapping between new ano_uno <-> uno
532 // create anonymous uno env
534 ::uno_createEnvironment( (uno_Environment
**)&aAnUno
, aUnoEnvTypeName
.pData
, 0 );
536 Mapping
aAnUno2Uno( getDirectMapping( aAnUno
, aUno
, rAddPurpose
) );
537 if (! aAnUno2Uno
.is())
540 if (aUno2To
.is()) // to is not uno
542 // create another purposed mediate mapping
543 aUno2To
= createMediateMapping( aAnUno
, rTo
, aAnUno2Uno
, aUno2To
, rAddPurpose
);
544 // : ano_uno <-> uno <-> to
548 aUno2To
= aAnUno2Uno
;
549 // : ano_uno <-> to (i.e., uno)
554 Mapping
aFrom2Uno( getDirectMapping( rFrom
, aUno
) );
555 if (aFrom2Uno
.is() && aUno2To
.is())
557 return createMediateMapping( rFrom
, rTo
, aFrom2Uno
, aUno2To
, rAddPurpose
);
558 // : from <-> some uno ...
565 using namespace ::cppu
;
569 //##################################################################################################
570 void SAL_CALL
uno_getMapping(
571 uno_Mapping
** ppMapping
, uno_Environment
* pFrom
, uno_Environment
* pTo
,
572 rtl_uString
* pAddPurpose
)
575 assert(ppMapping
!= 0);
580 (*(*ppMapping
)->release
)( *ppMapping
);
585 Environment
aFrom( pFrom
), aTo( pTo
);
587 OUString aAddPurpose
;
589 aAddPurpose
= pAddPurpose
;
591 MappingsData
& rData
= getMappingsData();
593 // try registered mapping
595 MutexGuard
aGuard( rData
.aMappingsMutex
);
596 const t_OUString2Entry::const_iterator
iFind( rData
.aName2Entry
.find(
597 getMappingName( aFrom
, aTo
, aAddPurpose
) ) );
598 if (iFind
!= rData
.aName2Entry
.end())
599 aRet
= (*iFind
).second
->pMapping
;
602 // See if an identity mapping does fit.
603 if (!aRet
.is() && pFrom
== pTo
&& aAddPurpose
.isEmpty())
604 aRet
= createIdentityMapping(pFrom
);
608 getCascadeMapping(ppMapping
, pFrom
, pTo
, pAddPurpose
);
614 if (! aRet
.is()) // try callback chain
616 MutexGuard
aGuard( rData
.aCallbacksMutex
);
617 for ( t_CallbackSet::const_iterator
iPos( rData
.aCallbacks
.begin() );
618 iPos
!= rData
.aCallbacks
.end(); ++iPos
)
620 (**iPos
)( ppMapping
, pFrom
, pTo
, aAddPurpose
.pData
);
628 aRet
= loadExternalMapping( aFrom
, aTo
, aAddPurpose
); // direct try
630 aRet
= getMediateMapping( aFrom
, aTo
, aAddPurpose
); // try via uno
635 (*aRet
.get()->acquire
)( aRet
.get() );
636 *ppMapping
= aRet
.get();
639 //##################################################################################################
640 void SAL_CALL
uno_getMappingByName(
641 uno_Mapping
** ppMapping
, rtl_uString
* pFrom
, rtl_uString
* pTo
,
642 rtl_uString
* pAddPurpose
)
645 OSL_ENSURE( ppMapping
&& pFrom
&& pTo
, "### null ptr!" );
648 (*(*ppMapping
)->release
)( *ppMapping
);
652 uno_Environment
* pEFrom
= 0;
653 uno_getEnvironment( &pEFrom
, pFrom
, 0 );
654 OSL_ENSURE( pEFrom
, "### cannot get source environment!" );
657 uno_Environment
* pETo
= 0;
658 uno_getEnvironment( &pETo
, pTo
, 0 );
659 OSL_ENSURE( pETo
, "### cannot get target environment!" );
662 ::uno_getMapping( ppMapping
, pEFrom
, pETo
, pAddPurpose
);
663 (*pETo
->release
)( pETo
);
665 (*pEFrom
->release
)( pEFrom
);
669 //##################################################################################################
670 CPPU_DLLPUBLIC
void SAL_CALL
uno_registerMapping(
671 uno_Mapping
** ppMapping
, uno_freeMappingFunc freeMapping
,
672 uno_Environment
* pFrom
, uno_Environment
* pTo
, rtl_uString
* pAddPurpose
)
675 MappingsData
& rData
= getMappingsData();
676 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
678 const t_Mapping2Entry::const_iterator
iFind( rData
.aMapping2Entry
.find( *ppMapping
) );
679 if (iFind
== rData
.aMapping2Entry
.end())
681 OUString
aMappingName(
682 getMappingName( pFrom
, pTo
, pAddPurpose
? OUString(pAddPurpose
) : OUString() ) );
683 #if OSL_DEBUG_LEVEL > 2
684 OString
cstr( OUStringToOString( aMappingName
, RTL_TEXTENCODING_ASCII_US
) );
685 OSL_TRACE( "> inserting new mapping: %s", cstr
.getStr() );
688 MappingEntry
* pEntry
= new MappingEntry( *ppMapping
, freeMapping
, aMappingName
);
689 rData
.aName2Entry
[ aMappingName
] = pEntry
;
690 rData
.aMapping2Entry
[ *ppMapping
] = pEntry
;
694 MappingEntry
* pEntry
= (*iFind
).second
;
697 if (pEntry
->pMapping
!= *ppMapping
) // exchange mapping to be registered
699 (*pEntry
->pMapping
->acquire
)( pEntry
->pMapping
);
700 --pEntry
->nRef
; // correct count; kill mapping to be registered
702 (*freeMapping
)( *ppMapping
);
703 *ppMapping
= pEntry
->pMapping
;
707 //##################################################################################################
708 CPPU_DLLPUBLIC
void SAL_CALL
uno_revokeMapping(
709 uno_Mapping
* pMapping
)
712 MappingsData
& rData
= getMappingsData();
713 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
715 const t_Mapping2Entry::const_iterator
iFind( rData
.aMapping2Entry
.find( pMapping
) );
716 OSL_ASSERT( iFind
!= rData
.aMapping2Entry
.end() );
717 MappingEntry
* pEntry
= (*iFind
).second
;
718 if (! --pEntry
->nRef
)
720 rData
.aMapping2Entry
.erase( pEntry
->pMapping
);
721 rData
.aName2Entry
.erase( pEntry
->aMappingName
);
723 #if OSL_DEBUG_LEVEL > 2
724 OString
cstr( OUStringToOString( pEntry
->aMappingName
, RTL_TEXTENCODING_ASCII_US
) );
725 OSL_TRACE( "> revoking mapping %s", cstr
.getStr() );
727 (*pEntry
->freeMapping
)( pEntry
->pMapping
);
732 //##################################################################################################
733 CPPU_DLLPUBLIC
void SAL_CALL
uno_registerMappingCallback(
734 uno_getMappingFunc pCallback
)
737 OSL_ENSURE( pCallback
, "### null ptr!" );
738 MappingsData
& rData
= getMappingsData();
739 MutexGuard
aGuard( rData
.aCallbacksMutex
);
740 rData
.aCallbacks
.insert( pCallback
);
742 //##################################################################################################
743 CPPU_DLLPUBLIC
void SAL_CALL
uno_revokeMappingCallback(
744 uno_getMappingFunc pCallback
)
747 OSL_ENSURE( pCallback
, "### null ptr!" );
748 MappingsData
& rData
= getMappingsData();
749 MutexGuard
aGuard( rData
.aCallbacksMutex
);
750 rData
.aCallbacks
.erase( pCallback
);
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */