Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/services/modulemanager.cxx
Line
Count
Source
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
10.3M
{
157
    // valid parameter?
158
10.3M
    css::uno::Reference< css::frame::XFrame >      xFrame     (xModule, css::uno::UNO_QUERY);
159
10.3M
    css::uno::Reference< css::awt::XWindow >       xWindow    (xModule, css::uno::UNO_QUERY);
160
10.3M
    css::uno::Reference< css::frame::XController > xController(xModule, css::uno::UNO_QUERY);
161
10.3M
    css::uno::Reference< css::frame::XModel >      xModel     (xModule, css::uno::UNO_QUERY);
162
163
10.3M
    if (
164
10.3M
        (!xFrame.is()     ) &&
165
0
        (!xWindow.is()    ) &&
166
0
        (!xController.is()) &&
167
0
        (!xModel.is()     )
168
10.3M
       )
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
10.3M
    if (xFrame.is())
177
10.3M
    {
178
10.3M
        xController = xFrame->getController();
179
10.3M
        xWindow     = xFrame->getComponentWindow();
180
10.3M
    }
181
10.3M
    if (xController.is())
182
147k
        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
10.3M
    OUString sModule;
190
10.3M
    if (xModel.is())
191
147k
        sModule = implts_identify(xModel);
192
10.2M
    else if (xController.is())
193
0
        sModule = implts_identify(xController);
194
10.2M
    else if (xWindow.is())
195
0
        sModule = implts_identify(xWindow);
196
197
10.3M
    if (sModule.isEmpty())
198
10.2M
        throw css::frame::UnknownModuleException(
199
10.2M
                u"Can not find suitable module for the given component."_ustr,
200
10.2M
                static_cast< ::cppu::OWeakObject* >(this));
201
202
147k
    return sModule;
203
10.3M
}
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
7.35k
{
249
    // get access to the element
250
7.35k
    css::uno::Reference< css::container::XNameAccess > xModule;
251
7.35k
    if (m_xCFG)
252
0
        m_xCFG->getByName(sName) >>= xModule;
253
7.35k
    if (!xModule.is())
254
7.35k
    {
255
7.35k
        throw css::uno::RuntimeException(
256
7.35k
                u"Was not able to get write access to the requested module entry inside configuration."_ustr,
257
7.35k
                static_cast< cppu::OWeakObject * >(this));
258
7.35k
    }
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
7.35k
}
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
147k
{
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
147k
    css::uno::Reference< css::frame::XModule > xModule(xComponent, css::uno::UNO_QUERY);
326
147k
    if (xModule.is())
327
147k
        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: */