Coverage Report

Created: 2025-07-07 10:01

/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: */