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"
27 #include <unordered_map>
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"
35 #include "sal/log.hxx"
37 #include "uno/dispatcher.h"
38 #include "uno/mapping.h"
39 #include "uno/lbnames.h"
40 #include "uno/environment.hxx"
42 #include "typelib/typedescription.h"
44 #include "cppu/EnvDcp.hxx"
45 #include "cascade_mapping.hxx"
46 #include "loadmodule.hxx"
50 using namespace com::sun::star::uno
;
51 using ::rtl::OUString
;
52 using ::rtl::OUStringBuffer
;
53 using ::rtl::OUStringToOString
;
54 using ::rtl::OUStringHash
;
62 uno_Mapping
* _pMapping
;
65 inline explicit Mapping( uno_Mapping
* pMapping
= 0 );
66 inline Mapping( const Mapping
& rMapping
);
68 inline Mapping
& SAL_CALL
operator = ( uno_Mapping
* pMapping
);
69 inline Mapping
& SAL_CALL
operator = ( const Mapping
& rMapping
)
70 { return operator = ( rMapping
._pMapping
); }
71 inline uno_Mapping
* SAL_CALL
get() const
73 inline bool SAL_CALL
is() const
74 { return (_pMapping
!= 0); }
77 inline Mapping::Mapping( uno_Mapping
* pMapping
)
78 : _pMapping( pMapping
)
81 (*_pMapping
->acquire
)( _pMapping
);
84 inline Mapping::Mapping( const Mapping
& rMapping
)
85 : _pMapping( rMapping
._pMapping
)
88 (*_pMapping
->acquire
)( _pMapping
);
91 inline Mapping::~Mapping()
94 (*_pMapping
->release
)( _pMapping
);
97 inline Mapping
& Mapping::operator = ( uno_Mapping
* pMapping
)
100 (*pMapping
->acquire
)( pMapping
);
102 (*_pMapping
->release
)( _pMapping
);
103 _pMapping
= pMapping
;
111 uno_Mapping
* pMapping
;
112 uno_freeMappingFunc freeMapping
;
113 OUString aMappingName
;
116 uno_Mapping
* pMapping_
, uno_freeMappingFunc freeMapping_
,
117 const OUString
& rMappingName_
)
119 , pMapping( pMapping_
)
120 , freeMapping( freeMapping_
)
121 , aMappingName( rMappingName_
)
125 struct FctPtrHash
: public std::unary_function
< uno_Mapping
*, size_t >
127 size_t operator()( uno_Mapping
* pKey
) const
128 { return reinterpret_cast<size_t>(pKey
); }
131 typedef std::unordered_map
<
132 OUString
, MappingEntry
*, OUStringHash
> t_OUString2Entry
;
133 typedef std::unordered_map
<
134 uno_Mapping
*, MappingEntry
*, FctPtrHash
, equal_to
< uno_Mapping
* > > t_Mapping2Entry
;
136 typedef set
< uno_getMappingFunc
> t_CallbackSet
;
137 typedef set
< OUString
> t_OUStringSet
;
142 Mutex aMappingsMutex
;
143 t_OUString2Entry aName2Entry
;
144 t_Mapping2Entry aMapping2Entry
;
146 Mutex aCallbacksMutex
;
147 t_CallbackSet aCallbacks
;
149 Mutex aNegativeLibsMutex
;
150 t_OUStringSet aNegativeLibs
;
153 static MappingsData
& getMappingsData()
155 static MappingsData
* s_p
= 0;
158 MutexGuard
aGuard( Mutex::getGlobalMutex() );
161 //TODO This memory is leaked; see #i63473# for when this should be
163 s_p
= new MappingsData
;
170 * This class mediates two different mapping via uno, e.g. form any language to uno,
171 * then from uno to any other language.
173 struct uno_Mediate_Mapping
: public uno_Mapping
183 OUString aAddPurpose
;
186 const Environment
& rFrom_
, const Environment
& rTo_
,
187 const Mapping
& rFrom2Uno_
, const Mapping
& rUno2To_
,
188 const OUString
& rAddPurpose
);
193 static void SAL_CALL
mediate_free( uno_Mapping
* pMapping
)
195 delete static_cast< uno_Mediate_Mapping
* >( pMapping
);
198 static void SAL_CALL
mediate_acquire( uno_Mapping
* pMapping
)
200 if (1 == osl_atomic_increment(
201 & static_cast< uno_Mediate_Mapping
* >( pMapping
)->nRef
))
204 &pMapping
, mediate_free
,
205 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aFrom
.get(),
206 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aTo
.get(),
207 static_cast< uno_Mediate_Mapping
* >( pMapping
)->aAddPurpose
.pData
);
211 static void SAL_CALL
mediate_release( uno_Mapping
* pMapping
)
213 if (! osl_atomic_decrement(
214 & static_cast< uno_Mediate_Mapping
* >( pMapping
)->nRef
))
216 uno_revokeMapping( pMapping
);
220 static void SAL_CALL
mediate_mapInterface(
221 uno_Mapping
* pMapping
,
222 void ** ppOut
, void * pInterface
,
223 typelib_InterfaceTypeDescription
* pInterfaceTypeDescr
)
225 OSL_ENSURE( pMapping
&& ppOut
, "### null ptr!" );
226 if (pMapping
&& ppOut
)
228 uno_Mediate_Mapping
* that
= static_cast< uno_Mediate_Mapping
* >( pMapping
);
229 uno_Mapping
* pFrom2Uno
= that
->aFrom2Uno
.get();
231 uno_Interface
* pUnoI
= 0;
232 (*pFrom2Uno
->mapInterface
)( pFrom2Uno
, reinterpret_cast<void **>(&pUnoI
), pInterface
, pInterfaceTypeDescr
);
235 void * pOut
= *ppOut
;
238 uno_ExtEnvironment
* pTo
= that
->aTo
.get()->pExtEnv
;
239 OSL_ENSURE( 0 != pTo
, "### cannot release out interface: leaking!" );
241 (*pTo
->releaseInterface
)( pTo
, pOut
);
242 *ppOut
= 0; // set to 0 anyway, because mapping was not successful!
247 uno_Mapping
* pUno2To
= that
->aUno2To
.get();
248 (*pUno2To
->mapInterface
)( pUno2To
, ppOut
, pUnoI
, pInterfaceTypeDescr
);
249 (*pUnoI
->release
)( pUnoI
);
255 uno_Mediate_Mapping::uno_Mediate_Mapping(
256 const Environment
& rFrom_
, const Environment
& rTo_
,
257 const Mapping
& rFrom2Uno_
, const Mapping
& rUno2To_
,
258 const OUString
& rAddPurpose_
)
262 , aFrom2Uno( rFrom2Uno_
)
263 , aUno2To( rUno2To_
)
264 , aAddPurpose( rAddPurpose_
)
266 uno_Mapping::acquire
= mediate_acquire
;
267 uno_Mapping::release
= mediate_release
;
268 uno_Mapping::mapInterface
= mediate_mapInterface
;
272 static inline OUString
getMappingName(
273 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
275 OUStringBuffer
aKey( 64 );
276 aKey
.append( rAddPurpose
);
278 aKey
.append( rFrom
.getTypeName() );
280 aKey
.append( reinterpret_cast< sal_IntPtr
>(rFrom
.get()), 16 );
282 aKey
.append( rTo
.getTypeName() );
284 aKey
.append( reinterpret_cast< sal_IntPtr
>(rTo
.get()), 16 );
286 return aKey
.makeStringAndClear();
289 static inline OUString
getBridgeName(
290 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
292 OUStringBuffer
aBridgeName( 16 );
293 if (!rAddPurpose
.isEmpty())
295 aBridgeName
.append( rAddPurpose
);
296 aBridgeName
.append( '_' );
298 aBridgeName
.append( EnvDcp::getTypeName(rFrom
.getTypeName()) );
299 aBridgeName
.append( '_' );
300 aBridgeName
.append( EnvDcp::getTypeName(rTo
.getTypeName()) );
301 return aBridgeName
.makeStringAndClear();
304 #ifndef DISABLE_DYNLOADING
306 static inline void setNegativeBridge( const OUString
& rBridgeName
)
308 MappingsData
& rData
= getMappingsData();
309 MutexGuard
aGuard( rData
.aNegativeLibsMutex
);
310 rData
.aNegativeLibs
.insert( rBridgeName
);
315 #ifdef DISABLE_DYNLOADING
317 static uno_ext_getMappingFunc
selectMapFunc( const OUString
& rBridgeName
)
319 if (rBridgeName
.equalsAscii( CPPU_CURRENT_LANGUAGE_BINDING_NAME
"_uno" ))
320 return CPPU_ENV_uno_ext_getMapping
;
321 #if HAVE_FEATURE_JAVA
322 if (rBridgeName
.equalsAscii( "java" "_uno" ))
323 return java_uno_ext_getMapping
;
327 // I don't think the affine or log bridges will be needed on any
328 // DISABLE_DYNLOADING platform (iOS at least, possibly Android), but if
329 // somebody wants to experiment, need to find out then whether these are
331 if (rBridgeName
.equalsAscii( "affine_uno_uno" ))
332 return affine_uno_uno_ext_getMapping
;
333 if (rBridgeName
.equalsAscii( "log_uno_uno" ))
334 return log_uno_uno_ext_getMapping
;
341 static inline bool loadModule(osl::Module
& rModule
, const OUString
& rBridgeName
)
345 MappingsData
& rData
= getMappingsData();
346 MutexGuard
aGuard( rData
.aNegativeLibsMutex
);
347 const t_OUStringSet::const_iterator
iFind( rData
.aNegativeLibs
.find( rBridgeName
) );
348 bNeg
= (iFind
!= rData
.aNegativeLibs
.end());
353 bool bModule
= cppu::detail::loadModule(rModule
, rBridgeName
);
358 setNegativeBridge( rBridgeName
); // no load again
366 static Mapping
loadExternalMapping(
367 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
369 OSL_ASSERT( rFrom
.is() && rTo
.is() );
370 if (rFrom
.is() && rTo
.is())
372 #ifdef DISABLE_DYNLOADING
374 uno_ext_getMappingFunc fpGetMapFunc
= 0;
376 if (EnvDcp::getTypeName(rFrom
.getTypeName()) == UNO_LB_UNO
)
378 aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
);
379 fpGetMapFunc
= selectMapFunc( aName
);
383 aName
= getBridgeName( rFrom
, rTo
, rAddPurpose
);
384 fpGetMapFunc
= selectMapFunc( aName
);
388 aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
);
389 fpGetMapFunc
= selectMapFunc( aName
);
394 SAL_INFO("cppu", "Could not find mapfunc for " << aName
);
401 (*fpGetMapFunc
)( (uno_Mapping
**)&aExt
, rFrom
.get(), rTo
.get() );
402 OSL_ASSERT( aExt
.is() );
412 if ( EnvDcp::getTypeName(rFrom
.getTypeName()) == UNO_LB_UNO
)
413 bModule
= loadModule( aModule
, aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
) );
415 bModule
= loadModule( aModule
, aName
= getBridgeName( rFrom
, rTo
, rAddPurpose
) );
417 bModule
= loadModule( aModule
, aName
= getBridgeName( rTo
, rFrom
, rAddPurpose
) );
421 OUString
aSymbolName( UNO_EXT_GETMAPPING
);
422 uno_ext_getMappingFunc fpGetMapFunc
=
423 reinterpret_cast<uno_ext_getMappingFunc
>(aModule
.getSymbol( aSymbolName
));
428 (*fpGetMapFunc
)( reinterpret_cast<uno_Mapping
**>(&aExt
), rFrom
.get(), rTo
.get() );
429 OSL_ASSERT( aExt
.is() );
437 setNegativeBridge( aName
);
445 static Mapping
getDirectMapping(
446 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
= OUString() )
449 OSL_ASSERT( rFrom
.is() && rTo
.is() );
450 if (rFrom
.is() && rTo
.is())
452 MappingsData
& rData
= getMappingsData();
453 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
455 // try to find registered mapping
456 const t_OUString2Entry::const_iterator
iFind( rData
.aName2Entry
.find(
457 getMappingName( rFrom
, rTo
, rAddPurpose
) ) );
459 if (iFind
== rData
.aName2Entry
.end())
462 return loadExternalMapping( rFrom
, rTo
, rAddPurpose
);
466 return Mapping( (*iFind
).second
->pMapping
);
473 static inline Mapping
createMediateMapping(
474 const Environment
& rFrom
, const Environment
& rTo
,
475 const Mapping
& rFrom2Uno
, const Mapping
& rUno2To
,
476 const OUString
& rAddPurpose
)
478 uno_Mapping
* pRet
= new uno_Mediate_Mapping(
479 rFrom
, rTo
, rFrom2Uno
, rUno2To
, rAddPurpose
); // ref count initially 1
481 &pRet
, mediate_free
, rFrom
.get(), rTo
.get(), rAddPurpose
.pData
);
482 Mapping
aRet( pRet
);
483 (*pRet
->release
)( pRet
);
487 static Mapping
getMediateMapping(
488 const Environment
& rFrom
, const Environment
& rTo
, const OUString
& rAddPurpose
)
493 // backwards: from dest to source of mapping chain
496 OUString
aUnoEnvTypeName( UNO_LB_UNO
);
497 if (rTo
.getTypeName() == aUnoEnvTypeName
) // to is uno
500 // no Uno2To mapping necessary
504 // get registered uno env
505 ::uno_getEnvironment( reinterpret_cast<uno_Environment
**>(&aUno
), aUnoEnvTypeName
.pData
, 0 );
507 aUno2To
= getDirectMapping( aUno
, rTo
);
514 if (!rAddPurpose
.isEmpty()) // insert purpose mapping between new ano_uno <-> uno
516 // create anonymous uno env
518 ::uno_createEnvironment( reinterpret_cast<uno_Environment
**>(&aAnUno
), aUnoEnvTypeName
.pData
, 0 );
520 Mapping
aAnUno2Uno( getDirectMapping( aAnUno
, aUno
, rAddPurpose
) );
521 if (! aAnUno2Uno
.is())
524 if (aUno2To
.is()) // to is not uno
526 // create another purposed mediate mapping
527 aUno2To
= createMediateMapping( aAnUno
, rTo
, aAnUno2Uno
, aUno2To
, rAddPurpose
);
528 // : ano_uno <-> uno <-> to
532 aUno2To
= aAnUno2Uno
;
533 // : ano_uno <-> to (i.e., uno)
538 Mapping
aFrom2Uno( getDirectMapping( rFrom
, aUno
) );
539 if (aFrom2Uno
.is() && aUno2To
.is())
541 return createMediateMapping( rFrom
, rTo
, aFrom2Uno
, aUno2To
, rAddPurpose
);
542 // : from <-> some uno ...
549 using namespace ::cppu
;
554 void SAL_CALL
uno_getMapping(
555 uno_Mapping
** ppMapping
, uno_Environment
* pFrom
, uno_Environment
* pTo
,
556 rtl_uString
* pAddPurpose
)
559 assert(ppMapping
!= 0);
564 (*(*ppMapping
)->release
)( *ppMapping
);
569 Environment
aFrom( pFrom
), aTo( pTo
);
571 OUString aAddPurpose
;
573 aAddPurpose
= pAddPurpose
;
575 MappingsData
& rData
= getMappingsData();
577 // try registered mapping
579 MutexGuard
aGuard( rData
.aMappingsMutex
);
580 const t_OUString2Entry::const_iterator
iFind( rData
.aName2Entry
.find(
581 getMappingName( aFrom
, aTo
, aAddPurpose
) ) );
582 if (iFind
!= rData
.aName2Entry
.end())
583 aRet
= (*iFind
).second
->pMapping
;
586 // See if an identity mapping does fit.
587 if (!aRet
.is() && pFrom
== pTo
&& aAddPurpose
.isEmpty())
588 aRet
= createIdentityMapping(pFrom
);
592 getCascadeMapping(ppMapping
, pFrom
, pTo
, pAddPurpose
);
598 if (! aRet
.is()) // try callback chain
600 MutexGuard
aGuard( rData
.aCallbacksMutex
);
601 for ( t_CallbackSet::const_iterator
iPos( rData
.aCallbacks
.begin() );
602 iPos
!= rData
.aCallbacks
.end(); ++iPos
)
604 (**iPos
)( ppMapping
, pFrom
, pTo
, aAddPurpose
.pData
);
612 aRet
= loadExternalMapping( aFrom
, aTo
, aAddPurpose
); // direct try
614 aRet
= getMediateMapping( aFrom
, aTo
, aAddPurpose
); // try via uno
619 (*aRet
.get()->acquire
)( aRet
.get() );
620 *ppMapping
= aRet
.get();
624 void SAL_CALL
uno_getMappingByName(
625 uno_Mapping
** ppMapping
, rtl_uString
* pFrom
, rtl_uString
* pTo
,
626 rtl_uString
* pAddPurpose
)
629 assert(ppMapping
&& pFrom
&& pTo
&& "### null ptr!");
632 (*(*ppMapping
)->release
)( *ppMapping
);
636 uno_Environment
* pEFrom
= 0;
637 uno_getEnvironment( &pEFrom
, pFrom
, 0 );
638 OSL_ENSURE( pEFrom
, "### cannot get source environment!" );
641 uno_Environment
* pETo
= 0;
642 uno_getEnvironment( &pETo
, pTo
, 0 );
643 OSL_ENSURE( pETo
, "### cannot get target environment!" );
646 ::uno_getMapping( ppMapping
, pEFrom
, pETo
, pAddPurpose
);
647 (*pETo
->release
)( pETo
);
649 (*pEFrom
->release
)( pEFrom
);
654 void SAL_CALL
uno_registerMapping(
655 uno_Mapping
** ppMapping
, uno_freeMappingFunc freeMapping
,
656 uno_Environment
* pFrom
, uno_Environment
* pTo
, rtl_uString
* pAddPurpose
)
659 MappingsData
& rData
= getMappingsData();
660 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
662 const t_Mapping2Entry::const_iterator
iFind( rData
.aMapping2Entry
.find( *ppMapping
) );
663 if (iFind
== rData
.aMapping2Entry
.end())
665 OUString
aMappingName(
666 getMappingName( pFrom
, pTo
, pAddPurpose
? OUString(pAddPurpose
) : OUString() ) );
667 SAL_INFO("cppu", "> inserting new mapping: " << aMappingName
);
669 MappingEntry
* pEntry
= new MappingEntry( *ppMapping
, freeMapping
, aMappingName
);
670 rData
.aName2Entry
[ aMappingName
] = pEntry
;
671 rData
.aMapping2Entry
[ *ppMapping
] = pEntry
;
675 MappingEntry
* pEntry
= (*iFind
).second
;
678 if (pEntry
->pMapping
!= *ppMapping
) // exchange mapping to be registered
680 (*pEntry
->pMapping
->acquire
)( pEntry
->pMapping
);
681 --pEntry
->nRef
; // correct count; kill mapping to be registered
683 (*freeMapping
)( *ppMapping
);
684 *ppMapping
= pEntry
->pMapping
;
689 void SAL_CALL
uno_revokeMapping(
690 uno_Mapping
* pMapping
)
693 MappingsData
& rData
= getMappingsData();
694 ClearableMutexGuard
aGuard( rData
.aMappingsMutex
);
696 const t_Mapping2Entry::const_iterator
iFind( rData
.aMapping2Entry
.find( pMapping
) );
697 OSL_ASSERT( iFind
!= rData
.aMapping2Entry
.end() );
698 MappingEntry
* pEntry
= (*iFind
).second
;
699 if (! --pEntry
->nRef
)
701 rData
.aMapping2Entry
.erase( pEntry
->pMapping
);
702 rData
.aName2Entry
.erase( pEntry
->aMappingName
);
704 SAL_INFO("cppu", "> revoking mapping " << pEntry
->aMappingName
);
705 (*pEntry
->freeMapping
)( pEntry
->pMapping
);
711 void SAL_CALL
uno_registerMappingCallback(
712 uno_getMappingFunc pCallback
)
715 OSL_ENSURE( pCallback
, "### null ptr!" );
716 MappingsData
& rData
= getMappingsData();
717 MutexGuard
aGuard( rData
.aCallbacksMutex
);
718 rData
.aCallbacks
.insert( pCallback
);
721 void SAL_CALL
uno_revokeMappingCallback(
722 uno_getMappingFunc pCallback
)
725 OSL_ENSURE( pCallback
, "### null ptr!" );
726 MappingsData
& rData
= getMappingsData();
727 MutexGuard
aGuard( rData
.aCallbacksMutex
);
728 rData
.aCallbacks
.erase( pCallback
);
732 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */