LibreOffice Module framework (master) 1
uielementfactorymanager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
25#include <helper/mischelper.hxx>
26
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>
40
41#include <rtl/ref.hxx>
42#include <sal/log.hxx>
47#include <utility>
48
49using namespace com::sun::star::uno;
50using namespace com::sun::star::lang;
51using namespace com::sun::star::beans;
52using namespace com::sun::star::frame;
53using namespace com::sun::star::configuration;
54using namespace com::sun::star::container;
55using namespace ::com::sun::star::ui;
56using namespace framework;
57
58namespace framework
59{
60
61// global function needed by both implementations
62static OUString getHashKeyFromStrings( std::u16string_view aType, std::u16string_view aName, std::u16string_view aModuleName )
63{
64 return OUString::Concat(aType) + "^" + aName + "^" + aModuleName;
65}
66
68 m_aPropType( "Type" ),
69 m_aPropName( "Name" ),
70 m_aPropModule( "Module" ),
71 m_aPropFactory( "FactoryImplementation" ),
72 m_sRoot(std::move(_sRoot)),
74{
75 m_xConfigProvider = theDefaultProvider::get( rxContext );
76}
77
79{
80 // SAFE
81 std::unique_lock g(m_aMutex);
82
83 Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
84 if ( xContainer.is() )
85 xContainer->removeContainerListener(m_xConfigListener);
86}
87
88OUString ConfigurationAccess_FactoryManager::getFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule ) const
89{
90 // SAFE
91 std::unique_lock g(m_aMutex);
92
93 FactoryManagerMap::const_iterator pIter =
94 m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, rName, rModule ));
95 if ( pIter != m_aFactoryManagerMap.end() )
96 return pIter->second;
97 else
98 {
99 pIter = m_aFactoryManagerMap.find(
100 getHashKeyFromStrings( rType, rName, std::u16string_view() ));
101 if ( pIter != m_aFactoryManagerMap.end() )
102 return pIter->second;
103 else
104 {
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)
108 {
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;
113 }
114
115 pIter = m_aFactoryManagerMap.find( getHashKeyFromStrings( rType, std::u16string_view(), std::u16string_view() ));
116 if ( pIter != m_aFactoryManagerMap.end() )
117 return pIter->second;
118 }
119 }
120
121 return OUString();
122}
123
124void ConfigurationAccess_FactoryManager::addFactorySpecifierToTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule, const OUString& rServiceSpecifier )
125{
126 // SAFE
127 std::unique_lock g(m_aMutex);
128
129 OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule );
130
131 FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey );
132
133 if ( pIter != m_aFactoryManagerMap.end() )
134 throw ElementExistException();
135 m_aFactoryManagerMap.emplace( aHashKey, rServiceSpecifier );
136}
137
138void ConfigurationAccess_FactoryManager::removeFactorySpecifierFromTypeNameModule( std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule )
139{
140 // SAFE
141 std::unique_lock g(m_aMutex);
142
143 OUString aHashKey = getHashKeyFromStrings( rType, rName, rModule );
144
145 FactoryManagerMap::const_iterator pIter = m_aFactoryManagerMap.find( aHashKey );
146
147 if ( pIter == m_aFactoryManagerMap.end() )
148 throw NoSuchElementException();
149 m_aFactoryManagerMap.erase( aHashKey );
150}
151
153{
154 // SAFE
155 std::unique_lock g(m_aMutex);
156
158
159 sal_Int32 nIndex( 0 );
160 for ( const auto& rEntry : m_aFactoryManagerMap )
161 {
162 OUString aFactory = rEntry.first;
163 if ( !aFactory.isEmpty() )
164 {
165 sal_Int32 nToken = 0;
166
167 aSeqSeq.realloc( aSeqSeq.getLength() + 1 );
169 m_aPropType, aFactory.getToken( 0, '^', nToken )) };
170 if ( nToken > 0 )
171 {
172 aSeq.realloc( 2 );
173 aSeq.getArray()[1]
175 aFactory.getToken( 0, '^', nToken ));
176 if ( nToken > 0 )
177 {
178 aSeq.realloc( 3 );
179 aSeq.getArray()[2]
181 aFactory.getToken( 0, '^', nToken ));
182 }
183 }
184
185 aSeqSeq.getArray()[nIndex++] = aSeq;
186 }
187 }
188
189 return aSeqSeq;
190}
191
192// container.XContainerListener
193void SAL_CALL ConfigurationAccess_FactoryManager::elementInserted( const ContainerEvent& aEvent )
194{
195 OUString aType;
196 OUString aName;
197 OUString aModule;
198 OUString aService;
199
200 // SAFE
201 std::unique_lock g(m_aMutex);
202
203 if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
204 {
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 );
209 }
210}
211
212void SAL_CALL ConfigurationAccess_FactoryManager::elementRemoved ( const ContainerEvent& aEvent )
213{
214 OUString aType;
215 OUString aName;
216 OUString aModule;
217 OUString aService;
218
219 // SAFE
220 std::unique_lock g(m_aMutex);
221
222 if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
223 {
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 );
228 }
229}
230
231void SAL_CALL ConfigurationAccess_FactoryManager::elementReplaced( const ContainerEvent& aEvent )
232{
233 OUString aType;
234 OUString aName;
235 OUString aModule;
236 OUString aService;
237
238 // SAFE
239 std::unique_lock g(m_aMutex);
240
241 if ( impl_getElementProps( aEvent.Element, aType, aName, aModule, aService ))
242 {
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 );
248 }
249}
250
251// lang.XEventListener
252void SAL_CALL ConfigurationAccess_FactoryManager::disposing( const EventObject& )
253{
254 // SAFE
255 // remove our reference to the config access
256 std::unique_lock g(m_aMutex);
257 m_xConfigAccess.clear();
258}
259
261{
262 // SAFE
263 std::unique_lock g(m_aMutex);
264
266 {
268 {
269 {"nodepath", Any(m_sRoot)}
270 }));
271
272 try
273 {
274 m_xConfigAccess.set( m_xConfigProvider->createInstanceWithArguments(
275 "com.sun.star.configuration.ConfigurationAccess", aArgs ), UNO_QUERY );
276 }
277 catch ( const WrappedTargetException& )
278 {
279 }
280
282 }
283
284 if ( !m_xConfigAccess.is() )
285 return;
286
287 const Sequence< OUString > aUIElementFactories = m_xConfigAccess->getElementNames();
288
289 OUString aType;
290 OUString aName;
291 OUString aModule;
292 OUString aService;
293 OUString aHashKey;
294 for ( OUString const & factoryName : aUIElementFactories )
295 {
296 if ( impl_getElementProps( m_xConfigAccess->getByName( factoryName ), aType, aName, aModule, aService ))
297 {
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 );
302 }
303 }
304
305 Reference< XContainer > xContainer( m_xConfigAccess, UNO_QUERY );
306 if ( xContainer.is() )
307 {
309 xContainer->addContainerListener(m_xConfigListener);
310 }
311}
312
313bool ConfigurationAccess_FactoryManager::impl_getElementProps( const Any& aElement, OUString& rType, OUString& rName, OUString& rModule, OUString& rServiceSpecifier ) const
314{
315 Reference< XPropertySet > xPropertySet;
316 aElement >>= xPropertySet;
317
318 if ( !xPropertySet.is() )
319 return true;
320
321 try
322 {
323 xPropertySet->getPropertyValue( m_aPropType ) >>= rType;
324 xPropertySet->getPropertyValue( m_aPropName ) >>= rName;
325 xPropertySet->getPropertyValue( m_aPropModule ) >>= rModule;
326 xPropertySet->getPropertyValue( m_aPropFactory ) >>= rServiceSpecifier;
327 }
328 catch ( const css::beans::UnknownPropertyException& )
329 {
330 return false;
331 }
332 catch ( const css::lang::WrappedTargetException& )
333 {
334 return false;
335 }
336
337 return true;
338}
339
340} // framework
341
342namespace {
343
345 css::lang::XServiceInfo,
346 css::ui::XUIElementFactoryManager> UIElementFactoryManager_BASE;
347
348class UIElementFactoryManager : public UIElementFactoryManager_BASE
349{
350 virtual void disposing(std::unique_lock<std::mutex>&) override;
351public:
352 explicit UIElementFactoryManager( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
353
354 virtual OUString SAL_CALL getImplementationName() override
355 {
356 return "com.sun.star.comp.framework.UIElementFactoryManager";
357 }
358
359 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
360 {
361 return cppu::supportsService(this, ServiceName);
362 }
363
364 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
365 {
366 return {"com.sun.star.ui.UIElementFactoryManager"};
367 }
368
369 // XUIElementFactory
370 virtual css::uno::Reference< css::ui::XUIElement > SAL_CALL createUIElement( const OUString& ResourceURL, const css::uno::Sequence< css::beans::PropertyValue >& Args ) override;
371
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;
377
378private:
379 bool m_bConfigRead;
380 css::uno::Reference< css::uno::XComponentContext > m_xContext;
382};
383
384UIElementFactoryManager::UIElementFactoryManager( const Reference< XComponentContext >& rxContext ) :
385 m_bConfigRead( false ),
386 m_xContext(rxContext),
387 m_pConfigAccess(
389 rxContext,
390 "/org.openoffice.Office.UI.Factories/Registered/UIElementFactories"))
391{}
392
393void UIElementFactoryManager::disposing(std::unique_lock<std::mutex>&)
394{
395 m_pConfigAccess.clear();
396}
397
398// XUIElementFactory
399Reference< XUIElement > SAL_CALL UIElementFactoryManager::createUIElement(
400 const OUString& ResourceURL,
401 const Sequence< PropertyValue >& Args )
402{
404 OUString aModuleId;
405 { // SAFE
406 std::unique_lock g(m_aMutex);
407 if (m_bDisposed) {
408 throw css::lang::DisposedException(
409 "disposed", static_cast<OWeakObject *>(this));
410 }
411
412 if ( !m_bConfigRead )
413 {
414 m_bConfigRead = true;
415 m_pConfigAccess->readConfigurationData();
416 }
417
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
420 // retrieve from it.
421 for ( auto const & arg : Args )
422 {
423 if ( arg.Name == "Frame")
424 arg.Value >>= xFrame;
425 if (arg.Name == "Module")
426 arg.Value >>= aModuleId;
427 }
428 } // SAFE
429
430 Reference< XModuleManager2 > xManager = ModuleManager::create( m_xContext );
431
432 // Determine the module identifier
433 try
434 {
435 if ( aModuleId.isEmpty() && xFrame.is() && xManager.is() )
436 aModuleId = xManager->identify( Reference<XInterface>( xFrame, UNO_QUERY ) );
437
438 Reference< XUIElementFactory > xUIElementFactory = getFactory( ResourceURL, aModuleId );
439 if ( xUIElementFactory.is() )
440 return xUIElementFactory->createUIElement( ResourceURL, Args );
441 }
442 catch ( const UnknownModuleException& )
443 {
444 }
445
446 throw NoSuchElementException();
447}
448
449// XUIElementFactoryRegistration
450Sequence< Sequence< PropertyValue > > SAL_CALL UIElementFactoryManager::getRegisteredFactories()
451{
452 // SAFE
453 std::unique_lock g(m_aMutex);
454 if (m_bDisposed) {
455 throw css::lang::DisposedException(
456 "disposed", static_cast<OWeakObject *>(this));
457 }
458
459 if ( !m_bConfigRead )
460 {
461 m_bConfigRead = true;
462 m_pConfigAccess->readConfigurationData();
463 }
464
465 return m_pConfigAccess->getFactoriesDescription();
466}
467
468Reference< XUIElementFactory > SAL_CALL UIElementFactoryManager::getFactory( const OUString& aResourceURL, const OUString& aModuleId )
469{
470 OUString aServiceSpecifier;
471 { // SAFE
472 std::unique_lock g(m_aMutex);
473 if (m_bDisposed) {
474 throw css::lang::DisposedException(
475 "disposed", static_cast<OWeakObject *>(this));
476 }
477
478 if ( !m_bConfigRead )
479 {
480 m_bConfigRead = true;
481 m_pConfigAccess->readConfigurationData();
482 }
483
484 OUString aType;
485 OUString aName;
486 RetrieveTypeNameFromResourceURL( aResourceURL, aType, aName );
487 aServiceSpecifier = m_pConfigAccess->getFactorySpecifierFromTypeNameModule( aType, aName, aModuleId );
488 } // SAFE
489
490 if ( !aServiceSpecifier.isEmpty() ) try
491 {
493 createInstanceWithContext(aServiceSpecifier, m_xContext), UNO_QUERY);
494 SAL_WARN_IF(!xFactory.is(), "fwk.uielement", "could not create factory: " << aServiceSpecifier);
495 return xFactory;
496 }
497 catch ( const css::loader::CannotActivateFactoryException& )
498 {
499 SAL_WARN("fwk.uielement", aServiceSpecifier <<
500 " not available. This should happen only on mobile platforms.");
501 }
503}
504
505void SAL_CALL UIElementFactoryManager::registerFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId, const OUString& aFactoryImplementationName )
506{
507 // SAFE
508 std::unique_lock g(m_aMutex);
509 if (m_bDisposed) {
510 throw css::lang::DisposedException(
511 "disposed", static_cast<OWeakObject *>(this));
512 }
513
514 if ( !m_bConfigRead )
515 {
516 m_bConfigRead = true;
517 m_pConfigAccess->readConfigurationData();
518 }
519
520 m_pConfigAccess->addFactorySpecifierToTypeNameModule( aType, aName, aModuleId, aFactoryImplementationName );
521 // SAFE
522}
523
524void SAL_CALL UIElementFactoryManager::deregisterFactory( const OUString& aType, const OUString& aName, const OUString& aModuleId )
525{
526 // SAFE
527 std::unique_lock g(m_aMutex);
528 if (m_bDisposed) {
529 throw css::lang::DisposedException(
530 "disposed", static_cast<OWeakObject *>(this));
531 }
532
533 if ( !m_bConfigRead )
534 {
535 m_bConfigRead = true;
536 m_pConfigAccess->readConfigurationData();
537 }
538
539 m_pConfigAccess->removeFactorySpecifierFromTypeNameModule( aType, aName, aModuleId );
540 // SAFE
541}
542
543}
544
545extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
547 css::uno::XComponentContext *context,
548 css::uno::Sequence<css::uno::Any> const &)
549{
550 return cppu::acquire(new UIElementFactoryManager(context));
551}
552
553/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
ConfigurationAccess_FactoryManager(const css::uno::Reference< css::uno::XComponentContext > &rxContext, OUString _sRoot)
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent &Event) override
virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent &Event) override
css::uno::Reference< css::container::XContainerListener > m_xConfigListener
css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > getFactoriesDescription() const
css::uno::Reference< css::container::XNameAccess > m_xConfigAccess
void removeFactorySpecifierFromTypeNameModule(std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule)
OUString getFactorySpecifierFromTypeNameModule(std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule) const
virtual void SAL_CALL elementInserted(const css::container::ContainerEvent &Event) override
bool impl_getElementProps(const css::uno::Any &rElement, OUString &rType, OUString &rName, OUString &rModule, OUString &rServiceSpecifier) const
css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider
void addFactorySpecifierToTypeNameModule(std::u16string_view rType, std::u16string_view rName, std::u16string_view rModule, const OUString &aServiceSpecifier)
Reference< XSingleServiceFactory > xFactory
bool m_bConfigRead
css::uno::Reference< css::uno::XComponentContext > m_xContext
bool m_bDisposed
sal_Int32 nIndex
OUString aName
Sequence< sal_Int8 > aSeq
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
static OUString getHashKeyFromStrings(std::u16string_view aCommandURL, std::u16string_view aModuleName)
void RetrieveTypeNameFromResourceURL(std::u16string_view aResourceURL, OUString &aType, OUString &aName)
Definition: mischelper.hxx:86
DefTokenId nToken
Reference< XFrame > xFrame
unsigned char sal_Bool
std::mutex m_aMutex
bool m_bConfigAccessInitialized
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_framework_UIElementFactoryManager_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)