/src/libreoffice/framework/source/services/modulemanager.cxx
Line | Count | Source (jump to first uncovered line) |
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 <com/sun/star/frame/UnknownModuleException.hpp> |
23 | | #include <com/sun/star/frame/XFrame.hpp> |
24 | | #include <com/sun/star/frame/XController.hpp> |
25 | | #include <com/sun/star/frame/XModel.hpp> |
26 | | #include <com/sun/star/frame/XModule.hpp> |
27 | | #include <com/sun/star/lang/XServiceInfo.hpp> |
28 | | #include <com/sun/star/frame/XModuleManager2.hpp> |
29 | | #include <com/sun/star/container/XNameReplace.hpp> |
30 | | #include <com/sun/star/container/XContainerQuery.hpp> |
31 | | #include <com/sun/star/uno/XComponentContext.hpp> |
32 | | |
33 | | #include <cppuhelper/implbase.hxx> |
34 | | #include <cppuhelper/supportsservice.hxx> |
35 | | #include <comphelper/configurationhelper.hxx> |
36 | | #include <comphelper/sequenceashashmap.hxx> |
37 | | #include <comphelper/sequence.hxx> |
38 | | #include <comphelper/enumhelper.hxx> |
39 | | #include <comphelper/configuration.hxx> |
40 | | #include <utility> |
41 | | |
42 | | namespace { |
43 | | |
44 | | class ModuleManager: |
45 | | public cppu::WeakImplHelper< |
46 | | css::lang::XServiceInfo, |
47 | | css::frame::XModuleManager2, |
48 | | css::container::XContainerQuery > |
49 | | { |
50 | | private: |
51 | | |
52 | | /** the global uno service manager. |
53 | | Must be used to create own needed services. |
54 | | */ |
55 | | css::uno::Reference< css::uno::XComponentContext > m_xContext; |
56 | | |
57 | | /** points to the underlying configuration. |
58 | | This ModuleManager does not cache - it calls directly the |
59 | | configuration API! |
60 | | */ |
61 | | css::uno::Reference< css::container::XNameAccess > m_xCFG; |
62 | | |
63 | | public: |
64 | | |
65 | | explicit ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext); |
66 | | |
67 | | ModuleManager(const ModuleManager&) = delete; |
68 | | ModuleManager& operator=(const ModuleManager&) = delete; |
69 | | |
70 | | // XServiceInfo |
71 | | virtual OUString SAL_CALL getImplementationName() override; |
72 | | |
73 | | virtual sal_Bool SAL_CALL supportsService( |
74 | | OUString const & ServiceName) override; |
75 | | |
76 | | virtual css::uno::Sequence< OUString > SAL_CALL |
77 | | getSupportedServiceNames() override; |
78 | | |
79 | | // XModuleManager |
80 | | virtual OUString SAL_CALL identify(const css::uno::Reference< css::uno::XInterface >& xModule) override; |
81 | | |
82 | | // XNameReplace |
83 | | virtual void SAL_CALL replaceByName(const OUString& sName , |
84 | | const css::uno::Any& aValue) override; |
85 | | |
86 | | // XNameAccess |
87 | | virtual css::uno::Any SAL_CALL getByName(const OUString& sName) override; |
88 | | |
89 | | virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; |
90 | | |
91 | | virtual sal_Bool SAL_CALL hasByName(const OUString& sName) override; |
92 | | |
93 | | // XElementAccess |
94 | | virtual css::uno::Type SAL_CALL getElementType() override; |
95 | | |
96 | | virtual sal_Bool SAL_CALL hasElements() override; |
97 | | |
98 | | // XContainerQuery |
99 | | virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByQuery(const OUString& sQuery) override; |
100 | | |
101 | | virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) override; |
102 | | |
103 | | private: |
104 | | |
105 | | /** @short makes the real identification of the module. |
106 | | |
107 | | @descr It checks for the optional but preferred interface |
108 | | XModule first. If this module does not exists at the |
109 | | given component it tries to use XServiceInfo instead. |
110 | | |
111 | | Note: This method try to locate a suitable module name. |
112 | | Nothing else. Selecting the right component and throwing suitable |
113 | | exceptions must be done outside. |
114 | | |
115 | | @see identify() |
116 | | |
117 | | @param xComponent |
118 | | the module for identification. |
119 | | |
120 | | @return The identifier of the given module. |
121 | | Can be empty if given component is not a real module ! |
122 | | |
123 | | @threadsafe |
124 | | */ |
125 | | OUString implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent); |
126 | | }; |
127 | | |
128 | | ModuleManager::ModuleManager(css::uno::Reference< css::uno::XComponentContext > xContext) |
129 | 2 | : m_xContext(std::move(xContext)) |
130 | 2 | { |
131 | 2 | if (!comphelper::IsFuzzing()) |
132 | 0 | { |
133 | 0 | m_xCFG.set( comphelper::ConfigurationHelper::openConfig( |
134 | 0 | m_xContext, u"/org.openoffice.Setup/Office/Factories"_ustr, |
135 | 0 | comphelper::EConfigurationModes::ReadOnly ), |
136 | 0 | css::uno::UNO_QUERY_THROW ); |
137 | 0 | } |
138 | 2 | } |
139 | | |
140 | | OUString ModuleManager::getImplementationName() |
141 | 0 | { |
142 | 0 | return u"com.sun.star.comp.framework.ModuleManager"_ustr; |
143 | 0 | } |
144 | | |
145 | | sal_Bool ModuleManager::supportsService(OUString const & ServiceName) |
146 | 0 | { |
147 | 0 | return cppu::supportsService(this, ServiceName); |
148 | 0 | } |
149 | | |
150 | | css::uno::Sequence< OUString > ModuleManager::getSupportedServiceNames() |
151 | 0 | { |
152 | 0 | return { u"com.sun.star.frame.ModuleManager"_ustr }; |
153 | 0 | } |
154 | | |
155 | | OUString SAL_CALL ModuleManager::identify(const css::uno::Reference< css::uno::XInterface >& xModule) |
156 | 25.2M | { |
157 | | // valid parameter? |
158 | 25.2M | css::uno::Reference< css::frame::XFrame > xFrame (xModule, css::uno::UNO_QUERY); |
159 | 25.2M | css::uno::Reference< css::awt::XWindow > xWindow (xModule, css::uno::UNO_QUERY); |
160 | 25.2M | css::uno::Reference< css::frame::XController > xController(xModule, css::uno::UNO_QUERY); |
161 | 25.2M | css::uno::Reference< css::frame::XModel > xModel (xModule, css::uno::UNO_QUERY); |
162 | | |
163 | 25.2M | if ( |
164 | 25.2M | (!xFrame.is() ) && |
165 | 25.2M | (!xWindow.is() ) && |
166 | 25.2M | (!xController.is()) && |
167 | 25.2M | (!xModel.is() ) |
168 | 25.2M | ) |
169 | 0 | { |
170 | 0 | throw css::lang::IllegalArgumentException( |
171 | 0 | u"Given module is not a frame nor a window, controller or model."_ustr, |
172 | 0 | static_cast< ::cppu::OWeakObject* >(this), |
173 | 0 | 1); |
174 | 0 | } |
175 | | |
176 | 25.2M | if (xFrame.is()) |
177 | 25.2M | { |
178 | 25.2M | xController = xFrame->getController(); |
179 | 25.2M | xWindow = xFrame->getComponentWindow(); |
180 | 25.2M | } |
181 | 25.2M | if (xController.is()) |
182 | 339k | xModel = xController->getModel(); |
183 | | |
184 | | // modules are implemented by the deepest component in hierarchy ... |
185 | | // Means: model -> controller -> window |
186 | | // No fallbacks to higher components are allowed ! |
187 | | // Note : A frame provides access to module components only ... but it's not a module by himself. |
188 | | |
189 | 25.2M | OUString sModule; |
190 | 25.2M | if (xModel.is()) |
191 | 339k | sModule = implts_identify(xModel); |
192 | 24.9M | else if (xController.is()) |
193 | 0 | sModule = implts_identify(xController); |
194 | 24.9M | else if (xWindow.is()) |
195 | 0 | sModule = implts_identify(xWindow); |
196 | | |
197 | 25.2M | if (sModule.isEmpty()) |
198 | 24.9M | throw css::frame::UnknownModuleException( |
199 | 24.9M | u"Can not find suitable module for the given component."_ustr, |
200 | 24.9M | static_cast< ::cppu::OWeakObject* >(this)); |
201 | | |
202 | 339k | return sModule; |
203 | 25.2M | } |
204 | | |
205 | | void SAL_CALL ModuleManager::replaceByName(const OUString& sName , |
206 | | const css::uno::Any& aValue) |
207 | 0 | { |
208 | 0 | ::comphelper::SequenceAsHashMap lProps(aValue); |
209 | 0 | if (lProps.empty() ) |
210 | 0 | { |
211 | 0 | throw css::lang::IllegalArgumentException( |
212 | 0 | u"No properties given to replace part of module."_ustr, |
213 | 0 | static_cast< cppu::OWeakObject * >(this), |
214 | 0 | 2); |
215 | 0 | } |
216 | | |
217 | | // get access to the element |
218 | | // Note: Don't use impl_getConfig() method here. Because it creates a readonly access only, further |
219 | | // it cache it as a member of this module manager instance. If we change some props there ... but don't |
220 | | // flush changes (because an error occurred) we will read them later. If we use a different config access |
221 | | // we can close it without a flush... and our read data won't be affected .-) |
222 | 0 | css::uno::Reference< css::uno::XInterface > xCfg = ::comphelper::ConfigurationHelper::openConfig( |
223 | 0 | m_xContext, |
224 | 0 | u"/org.openoffice.Setup/Office/Factories"_ustr, |
225 | 0 | ::comphelper::EConfigurationModes::Standard); |
226 | 0 | css::uno::Reference< css::container::XNameAccess > xModules (xCfg, css::uno::UNO_QUERY_THROW); |
227 | 0 | css::uno::Reference< css::container::XNameReplace > xModule ; |
228 | |
|
229 | 0 | xModules->getByName(sName) >>= xModule; |
230 | 0 | if (!xModule.is()) |
231 | 0 | { |
232 | 0 | throw css::uno::RuntimeException( |
233 | 0 | u"Was not able to get write access to the requested module entry inside configuration."_ustr, |
234 | 0 | static_cast< cppu::OWeakObject * >(this)); |
235 | 0 | } |
236 | | |
237 | 0 | for (auto const& prop : lProps) |
238 | 0 | { |
239 | | // let "NoSuchElementException" out ! We support the same API ... |
240 | | // and without a flush() at the end all changed data before will be ignored ! |
241 | 0 | xModule->replaceByName(prop.first.maString, prop.second); |
242 | 0 | } |
243 | |
|
244 | 0 | ::comphelper::ConfigurationHelper::flush(xCfg); |
245 | 0 | } |
246 | | |
247 | | css::uno::Any SAL_CALL ModuleManager::getByName(const OUString& sName) |
248 | 16.9k | { |
249 | | // get access to the element |
250 | 16.9k | css::uno::Reference< css::container::XNameAccess > xModule; |
251 | 16.9k | if (m_xCFG) |
252 | 0 | m_xCFG->getByName(sName) >>= xModule; |
253 | 16.9k | if (!xModule.is()) |
254 | 16.9k | { |
255 | 16.9k | throw css::uno::RuntimeException( |
256 | 16.9k | u"Was not able to get write access to the requested module entry inside configuration."_ustr, |
257 | 16.9k | static_cast< cppu::OWeakObject * >(this)); |
258 | 16.9k | } |
259 | | |
260 | | // convert it to seq< PropertyValue > |
261 | 0 | const css::uno::Sequence< OUString > lPropNames = xModule->getElementNames(); |
262 | 0 | comphelper::SequenceAsHashMap lProps; |
263 | |
|
264 | 0 | lProps[u"ooSetupFactoryModuleIdentifier"_ustr] <<= sName; |
265 | 0 | for (const OUString& sPropName : lPropNames) |
266 | 0 | { |
267 | 0 | lProps[sPropName] = xModule->getByName(sPropName); |
268 | 0 | } |
269 | |
|
270 | 0 | return css::uno::Any(lProps.getAsConstPropertyValueList()); |
271 | 16.9k | } |
272 | | |
273 | | css::uno::Sequence< OUString > SAL_CALL ModuleManager::getElementNames() |
274 | 4 | { |
275 | 4 | return m_xCFG ? m_xCFG->getElementNames() : css::uno::Sequence<OUString>(); |
276 | 4 | } |
277 | | |
278 | | sal_Bool SAL_CALL ModuleManager::hasByName(const OUString& sName) |
279 | 0 | { |
280 | 0 | return m_xCFG && m_xCFG->hasByName(sName); |
281 | 0 | } |
282 | | |
283 | | css::uno::Type SAL_CALL ModuleManager::getElementType() |
284 | 0 | { |
285 | 0 | return cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(); |
286 | 0 | } |
287 | | |
288 | | sal_Bool SAL_CALL ModuleManager::hasElements() |
289 | 0 | { |
290 | 0 | return m_xCFG && m_xCFG->hasElements(); |
291 | 0 | } |
292 | | |
293 | | css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByQuery(const OUString&) |
294 | 0 | { |
295 | 0 | return css::uno::Reference< css::container::XEnumeration >(); |
296 | 0 | } |
297 | | |
298 | | css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) |
299 | 0 | { |
300 | 0 | ::comphelper::SequenceAsHashMap lSearchProps(lProperties); |
301 | 0 | const css::uno::Sequence< OUString > lModules = getElementNames(); |
302 | 0 | ::std::vector< css::uno::Any > lResult; |
303 | |
|
304 | 0 | for (const OUString& rModuleName : lModules) |
305 | 0 | { |
306 | 0 | try |
307 | 0 | { |
308 | 0 | ::comphelper::SequenceAsHashMap lModuleProps = getByName(rModuleName); |
309 | 0 | if (lModuleProps.match(lSearchProps)) |
310 | 0 | lResult.push_back(css::uno::Any(lModuleProps.getAsConstPropertyValueList())); |
311 | 0 | } |
312 | 0 | catch(const css::uno::Exception&) |
313 | 0 | { |
314 | 0 | } |
315 | 0 | } |
316 | |
|
317 | 0 | return new ::comphelper::OAnyEnumeration(comphelper::containerToSequence(lResult)); |
318 | 0 | } |
319 | | |
320 | | OUString ModuleManager::implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent) |
321 | 339k | { |
322 | | // Search for an optional (!) interface XModule first. |
323 | | // It's used to overrule an existing service name. Used e.g. by our database form designer |
324 | | // which uses a writer module internally. |
325 | 339k | css::uno::Reference< css::frame::XModule > xModule(xComponent, css::uno::UNO_QUERY); |
326 | 339k | if (xModule.is()) |
327 | 339k | return xModule->getIdentifier(); |
328 | | |
329 | | // detect modules in a generic way... |
330 | | // comparing service names with configured entries... |
331 | 0 | css::uno::Reference< css::lang::XServiceInfo > xInfo(xComponent, css::uno::UNO_QUERY); |
332 | 0 | if (!xInfo.is()) |
333 | 0 | return OUString(); |
334 | | |
335 | 0 | const css::uno::Sequence< OUString > lKnownModules = getElementNames(); |
336 | 0 | for (const OUString& rName : lKnownModules) |
337 | 0 | { |
338 | 0 | if (xInfo->supportsService(rName)) |
339 | 0 | return rName; |
340 | 0 | } |
341 | | |
342 | 0 | return OUString(); |
343 | 0 | } |
344 | | |
345 | | } |
346 | | |
347 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * |
348 | | com_sun_star_comp_framework_ModuleManager_get_implementation( |
349 | | css::uno::XComponentContext *context, |
350 | | css::uno::Sequence<css::uno::Any> const &) |
351 | 2 | { |
352 | 2 | return cppu::acquire(new ModuleManager(context)); |
353 | 2 | } |
354 | | |
355 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |