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 <sal/config.h>
22 #include <string_view>
24 #include <uifactory/configurationaccessfactorymanager.hxx>
25 #include <helper/mischelper.hxx>
27 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/configuration/theDefaultProvider.hpp>
30 #include <com/sun/star/container/ElementExistException.hpp>
31 #include <com/sun/star/container/XContainer.hpp>
32 #include <com/sun/star/container/XContainerListener.hpp>
33 #include <com/sun/star/lang/XServiceInfo.hpp>
34 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
35 #include <com/sun/star/frame/ModuleManager.hpp>
36 #include <com/sun/star/frame/UnknownModuleException.hpp>
37 #include <com/sun/star/frame/XFrame.hpp>
38 #include <com/sun/star/frame/XModuleManager2.hpp>
39 #include <com/sun/star/ui/XUIElementFactoryManager.hpp>
41 #include <rtl/ref.hxx>
42 #include <sal/log.hxx>
43 #include <comphelper/propertysequence.hxx>
44 #include <comphelper/propertyvalue.hxx>
45 #include <comphelper/compbase.hxx>
46 #include <cppuhelper/supportsservice.hxx>
49 using namespace com::sun::star::uno
;
50 using namespace com::sun::star::lang
;
51 using namespace com::sun::star::beans
;
52 using namespace com::sun::star::frame
;
53 using namespace com::sun::star::configuration
;
54 using namespace com::sun::star::container
;
55 using namespace ::com::sun::star::ui
;
56 using namespace framework
;
61 // global function needed by both implementations
62 static OUString
getHashKeyFromStrings( std::u16string_view aType
, std::u16string_view aName
, std::u16string_view aModuleName
)
64 return OUString::Concat(aType
) + "^" + aName
+ "^" + aModuleName
;
67 ConfigurationAccess_FactoryManager::ConfigurationAccess_FactoryManager( const Reference
< XComponentContext
>& rxContext
, OUString _sRoot
) :
68 m_aPropType( "Type" ),
69 m_aPropName( "Name" ),
70 m_aPropModule( "Module" ),
71 m_aPropFactory( "FactoryImplementation" ),
72 m_sRoot(std::move(_sRoot
)),
73 m_bConfigAccessInitialized( false )
75 m_xConfigProvider
= theDefaultProvider::get( rxContext
);
78 ConfigurationAccess_FactoryManager::~ConfigurationAccess_FactoryManager()
81 std::unique_lock
g(m_aMutex
);
83 Reference
< XContainer
> xContainer( m_xConfigAccess
, UNO_QUERY
);
84 if ( xContainer
.is() )
85 xContainer
->removeContainerListener(m_xConfigListener
);
88 OUString
ConfigurationAccess_FactoryManager::getFactorySpecifierFromTypeNameModule( std::u16string_view rType
, std::u16string_view rName
, std::u16string_view rModule
) const
91 std::unique_lock
g(m_aMutex
);
93 FactoryManagerMap::const_iterator pIter
=
94 m_aFactoryManagerMap
.find( getHashKeyFromStrings( rType
, rName
, rModule
));
95 if ( pIter
!= m_aFactoryManagerMap
.end() )
99 pIter
= m_aFactoryManagerMap
.find(
100 getHashKeyFromStrings( rType
, rName
, std::u16string_view() ));
101 if ( pIter
!= m_aFactoryManagerMap
.end() )
102 return pIter
->second
;
105 // Support factories which uses a defined prefix before the ui name.
106 size_t nIndex
= rName
.find( '_' );
107 if ( nIndex
> 0 && nIndex
!= std::u16string_view::npos
)
109 std::u16string_view aName
= rName
.substr( 0, nIndex
+1 );
110 pIter
= m_aFactoryManagerMap
.find( getHashKeyFromStrings( rType
, aName
, std::u16string_view() ));
111 if ( pIter
!= m_aFactoryManagerMap
.end() )
112 return pIter
->second
;
115 pIter
= m_aFactoryManagerMap
.find( getHashKeyFromStrings( rType
, std::u16string_view(), std::u16string_view() ));
116 if ( pIter
!= m_aFactoryManagerMap
.end() )
117 return pIter
->second
;
124 void ConfigurationAccess_FactoryManager::addFactorySpecifierToTypeNameModule( std::u16string_view rType
, std::u16string_view rName
, std::u16string_view rModule
, const OUString
& rServiceSpecifier
)
127 std::unique_lock
g(m_aMutex
);
129 OUString aHashKey
= getHashKeyFromStrings( rType
, rName
, rModule
);
131 FactoryManagerMap::const_iterator pIter
= m_aFactoryManagerMap
.find( aHashKey
);
133 if ( pIter
!= m_aFactoryManagerMap
.end() )
134 throw ElementExistException();
135 m_aFactoryManagerMap
.emplace( aHashKey
, rServiceSpecifier
);
138 void ConfigurationAccess_FactoryManager::removeFactorySpecifierFromTypeNameModule( std::u16string_view rType
, std::u16string_view rName
, std::u16string_view rModule
)
141 std::unique_lock
g(m_aMutex
);
143 OUString aHashKey
= getHashKeyFromStrings( rType
, rName
, rModule
);
145 FactoryManagerMap::const_iterator pIter
= m_aFactoryManagerMap
.find( aHashKey
);
147 if ( pIter
== m_aFactoryManagerMap
.end() )
148 throw NoSuchElementException();
149 m_aFactoryManagerMap
.erase( aHashKey
);
152 Sequence
< Sequence
< PropertyValue
> > ConfigurationAccess_FactoryManager::getFactoriesDescription() const
155 std::unique_lock
g(m_aMutex
);
157 Sequence
< Sequence
< PropertyValue
> > aSeqSeq
;
159 sal_Int32
nIndex( 0 );
160 for ( const auto& rEntry
: m_aFactoryManagerMap
)
162 OUString aFactory
= rEntry
.first
;
163 if ( !aFactory
.isEmpty() )
165 sal_Int32 nToken
= 0;
167 aSeqSeq
.realloc( aSeqSeq
.getLength() + 1 );
168 Sequence
< PropertyValue
> aSeq
{ comphelper::makePropertyValue(
169 m_aPropType
, aFactory
.getToken( 0, '^', nToken
)) };
174 = comphelper::makePropertyValue(m_aPropName
,
175 aFactory
.getToken( 0, '^', nToken
));
180 = comphelper::makePropertyValue(m_aPropModule
,
181 aFactory
.getToken( 0, '^', nToken
));
185 aSeqSeq
.getArray()[nIndex
++] = aSeq
;
192 // container.XContainerListener
193 void SAL_CALL
ConfigurationAccess_FactoryManager::elementInserted( const ContainerEvent
& aEvent
)
201 std::unique_lock
g(m_aMutex
);
203 if ( impl_getElementProps( aEvent
.Element
, aType
, aName
, aModule
, aService
))
205 // Create hash key from type, name and module as they are together a primary key to
206 // the UNO service that implements a user interface factory.
207 OUString
aHashKey( getHashKeyFromStrings( aType
, aName
, aModule
));
208 m_aFactoryManagerMap
.emplace( aHashKey
, aService
);
212 void SAL_CALL
ConfigurationAccess_FactoryManager::elementRemoved ( const ContainerEvent
& aEvent
)
220 std::unique_lock
g(m_aMutex
);
222 if ( impl_getElementProps( aEvent
.Element
, aType
, aName
, aModule
, aService
))
224 // Create hash key from command and model as they are together a primary key to
225 // the UNO service that implements the popup menu controller.
226 OUString
aHashKey( getHashKeyFromStrings( aType
, aName
, aModule
));
227 m_aFactoryManagerMap
.erase( aHashKey
);
231 void SAL_CALL
ConfigurationAccess_FactoryManager::elementReplaced( const ContainerEvent
& aEvent
)
239 std::unique_lock
g(m_aMutex
);
241 if ( impl_getElementProps( aEvent
.Element
, aType
, aName
, aModule
, aService
))
243 // Create hash key from command and model as they are together a primary key to
244 // the UNO service that implements the popup menu controller.
245 OUString
aHashKey( getHashKeyFromStrings( aType
, aName
, aModule
));
246 m_aFactoryManagerMap
.erase( aHashKey
);
247 m_aFactoryManagerMap
.emplace( aHashKey
, aService
);
251 // lang.XEventListener
252 void SAL_CALL
ConfigurationAccess_FactoryManager::disposing( const EventObject
& )
255 // remove our reference to the config access
256 std::unique_lock
g(m_aMutex
);
257 m_xConfigAccess
.clear();
260 void ConfigurationAccess_FactoryManager::readConfigurationData()
263 std::unique_lock
g(m_aMutex
);
265 if ( !m_bConfigAccessInitialized
)
267 Sequence
<Any
> aArgs(comphelper::InitAnyPropertySequence(
269 {"nodepath", Any(m_sRoot
)}
274 m_xConfigAccess
.set( m_xConfigProvider
->createInstanceWithArguments(
275 "com.sun.star.configuration.ConfigurationAccess", aArgs
), UNO_QUERY
);
277 catch ( const WrappedTargetException
& )
281 m_bConfigAccessInitialized
= true;
284 if ( !m_xConfigAccess
.is() )
287 const Sequence
< OUString
> aUIElementFactories
= m_xConfigAccess
->getElementNames();
294 for ( OUString
const & factoryName
: aUIElementFactories
)
296 if ( impl_getElementProps( m_xConfigAccess
->getByName( factoryName
), aType
, aName
, aModule
, aService
))
298 // Create hash key from type, name and module as they are together a primary key to
299 // the UNO service that implements the user interface element factory.
300 aHashKey
= getHashKeyFromStrings( aType
, aName
, aModule
);
301 m_aFactoryManagerMap
.emplace( aHashKey
, aService
);
305 Reference
< XContainer
> xContainer( m_xConfigAccess
, UNO_QUERY
);
306 if ( xContainer
.is() )
308 m_xConfigListener
= new WeakContainerListener(this);
309 xContainer
->addContainerListener(m_xConfigListener
);
313 bool ConfigurationAccess_FactoryManager::impl_getElementProps( const Any
& aElement
, OUString
& rType
, OUString
& rName
, OUString
& rModule
, OUString
& rServiceSpecifier
) const
315 Reference
< XPropertySet
> xPropertySet
;
316 aElement
>>= xPropertySet
;
318 if ( !xPropertySet
.is() )
323 xPropertySet
->getPropertyValue( m_aPropType
) >>= rType
;
324 xPropertySet
->getPropertyValue( m_aPropName
) >>= rName
;
325 xPropertySet
->getPropertyValue( m_aPropModule
) >>= rModule
;
326 xPropertySet
->getPropertyValue( m_aPropFactory
) >>= rServiceSpecifier
;
328 catch ( const css::beans::UnknownPropertyException
& )
332 catch ( const css::lang::WrappedTargetException
& )
344 typedef comphelper::WeakComponentImplHelper
<
345 css::lang::XServiceInfo
,
346 css::ui::XUIElementFactoryManager
> UIElementFactoryManager_BASE
;
348 class UIElementFactoryManager
: public UIElementFactoryManager_BASE
350 virtual void disposing(std::unique_lock
<std::mutex
>&) override
;
352 explicit UIElementFactoryManager( const css::uno::Reference
< css::uno::XComponentContext
>& rxContext
);
354 virtual OUString SAL_CALL
getImplementationName() override
356 return "com.sun.star.comp.framework.UIElementFactoryManager";
359 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
361 return cppu::supportsService(this, ServiceName
);
364 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
366 return {"com.sun.star.ui.UIElementFactoryManager"};
370 virtual css::uno::Reference
< css::ui::XUIElement
> SAL_CALL
createUIElement( const OUString
& ResourceURL
, const css::uno::Sequence
< css::beans::PropertyValue
>& Args
) override
;
372 // XUIElementFactoryRegistration
373 virtual css::uno::Sequence
< css::uno::Sequence
< css::beans::PropertyValue
> > SAL_CALL
getRegisteredFactories( ) override
;
374 virtual css::uno::Reference
< css::ui::XUIElementFactory
> SAL_CALL
getFactory( const OUString
& ResourceURL
, const OUString
& ModuleIdentifier
) override
;
375 virtual void SAL_CALL
registerFactory( const OUString
& aType
, const OUString
& aName
, const OUString
& aModuleIdentifier
, const OUString
& aFactoryImplementationName
) override
;
376 virtual void SAL_CALL
deregisterFactory( const OUString
& aType
, const OUString
& aName
, const OUString
& aModuleIdentifier
) override
;
380 css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
381 rtl::Reference
<ConfigurationAccess_FactoryManager
> m_pConfigAccess
;
384 UIElementFactoryManager::UIElementFactoryManager( const Reference
< XComponentContext
>& rxContext
) :
385 m_bConfigRead( false ),
386 m_xContext(rxContext
),
388 new ConfigurationAccess_FactoryManager(
390 "/org.openoffice.Office.UI.Factories/Registered/UIElementFactories"))
393 void UIElementFactoryManager::disposing(std::unique_lock
<std::mutex
>&)
395 m_pConfigAccess
.clear();
399 Reference
< XUIElement
> SAL_CALL
UIElementFactoryManager::createUIElement(
400 const OUString
& ResourceURL
,
401 const Sequence
< PropertyValue
>& Args
)
403 Reference
< XFrame
> xFrame
;
406 std::unique_lock
g(m_aMutex
);
408 throw css::lang::DisposedException(
409 "disposed", static_cast<OWeakObject
*>(this));
412 if ( !m_bConfigRead
)
414 m_bConfigRead
= true;
415 m_pConfigAccess
->readConfigurationData();
418 // Retrieve the frame instance from the arguments to determine the module identifier. This must be provided
419 // to the search function. An empty module identifier is provided if the frame is missing or the module id cannot
421 for ( auto const & arg
: Args
)
423 if ( arg
.Name
== "Frame")
424 arg
.Value
>>= xFrame
;
425 if (arg
.Name
== "Module")
426 arg
.Value
>>= aModuleId
;
430 Reference
< XModuleManager2
> xManager
= ModuleManager::create( m_xContext
);
432 // Determine the module identifier
435 if ( aModuleId
.isEmpty() && xFrame
.is() && xManager
.is() )
436 aModuleId
= xManager
->identify( Reference
<XInterface
>( xFrame
, UNO_QUERY
) );
438 Reference
< XUIElementFactory
> xUIElementFactory
= getFactory( ResourceURL
, aModuleId
);
439 if ( xUIElementFactory
.is() )
440 return xUIElementFactory
->createUIElement( ResourceURL
, Args
);
442 catch ( const UnknownModuleException
& )
446 throw NoSuchElementException();
449 // XUIElementFactoryRegistration
450 Sequence
< Sequence
< PropertyValue
> > SAL_CALL
UIElementFactoryManager::getRegisteredFactories()
453 std::unique_lock
g(m_aMutex
);
455 throw css::lang::DisposedException(
456 "disposed", static_cast<OWeakObject
*>(this));
459 if ( !m_bConfigRead
)
461 m_bConfigRead
= true;
462 m_pConfigAccess
->readConfigurationData();
465 return m_pConfigAccess
->getFactoriesDescription();
468 Reference
< XUIElementFactory
> SAL_CALL
UIElementFactoryManager::getFactory( const OUString
& aResourceURL
, const OUString
& aModuleId
)
470 OUString aServiceSpecifier
;
472 std::unique_lock
g(m_aMutex
);
474 throw css::lang::DisposedException(
475 "disposed", static_cast<OWeakObject
*>(this));
478 if ( !m_bConfigRead
)
480 m_bConfigRead
= true;
481 m_pConfigAccess
->readConfigurationData();
486 RetrieveTypeNameFromResourceURL( aResourceURL
, aType
, aName
);
487 aServiceSpecifier
= m_pConfigAccess
->getFactorySpecifierFromTypeNameModule( aType
, aName
, aModuleId
);
490 if ( !aServiceSpecifier
.isEmpty() ) try
492 Reference
< XUIElementFactory
> xFactory(m_xContext
->getServiceManager()->
493 createInstanceWithContext(aServiceSpecifier
, m_xContext
), UNO_QUERY
);
494 SAL_WARN_IF(!xFactory
.is(), "fwk.uielement", "could not create factory: " << aServiceSpecifier
);
497 catch ( const css::loader::CannotActivateFactoryException
& )
499 SAL_WARN("fwk.uielement", aServiceSpecifier
<<
500 " not available. This should happen only on mobile platforms.");
502 return Reference
< XUIElementFactory
>();
505 void SAL_CALL
UIElementFactoryManager::registerFactory( const OUString
& aType
, const OUString
& aName
, const OUString
& aModuleId
, const OUString
& aFactoryImplementationName
)
508 std::unique_lock
g(m_aMutex
);
510 throw css::lang::DisposedException(
511 "disposed", static_cast<OWeakObject
*>(this));
514 if ( !m_bConfigRead
)
516 m_bConfigRead
= true;
517 m_pConfigAccess
->readConfigurationData();
520 m_pConfigAccess
->addFactorySpecifierToTypeNameModule( aType
, aName
, aModuleId
, aFactoryImplementationName
);
524 void SAL_CALL
UIElementFactoryManager::deregisterFactory( const OUString
& aType
, const OUString
& aName
, const OUString
& aModuleId
)
527 std::unique_lock
g(m_aMutex
);
529 throw css::lang::DisposedException(
530 "disposed", static_cast<OWeakObject
*>(this));
533 if ( !m_bConfigRead
)
535 m_bConfigRead
= true;
536 m_pConfigAccess
->readConfigurationData();
539 m_pConfigAccess
->removeFactorySpecifierFromTypeNameModule( aType
, aName
, aModuleId
);
545 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
546 com_sun_star_comp_framework_UIElementFactoryManager_get_implementation(
547 css::uno::XComponentContext
*context
,
548 css::uno::Sequence
<css::uno::Any
> const &)
550 return cppu::acquire(new UIElementFactoryManager(context
));
553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */