2bafc7384b
and cid#1545841 COPY_INSTEAD_OF_MOVE cid#1554682 COPY_INSTEAD_OF_MOVE cid#1554686 COPY_INSTEAD_OF_MOVE cid#1554715 COPY_INSTEAD_OF_MOVE cid#1554750 COPY_INSTEAD_OF_MOVE cid#1554759 COPY_INSTEAD_OF_MOVE cid#1554770 COPY_INSTEAD_OF_MOVE cid#1554779 COPY_INSTEAD_OF_MOVE cid#1554794 COPY_INSTEAD_OF_MOVE cid#1554800 COPY_INSTEAD_OF_MOVE cid#1554826 COPY_INSTEAD_OF_MOVE cid#1554836 COPY_INSTEAD_OF_MOVE cid#1554862 COPY_INSTEAD_OF_MOVE cid#1554865 COPY_INSTEAD_OF_MOVE cid#1554872 COPY_INSTEAD_OF_MOVE cid#1554883 COPY_INSTEAD_OF_MOVE cid#1554906 COPY_INSTEAD_OF_MOVE cid#1554921 COPY_INSTEAD_OF_MOVE cid#1554926 COPY_INSTEAD_OF_MOVE cid#1554946 COPY_INSTEAD_OF_MOVE cid#1554956 COPY_INSTEAD_OF_MOVE cid#1554970 COPY_INSTEAD_OF_MOVE cid#1554986 COPY_INSTEAD_OF_MOVE cid#1554991 COPY_INSTEAD_OF_MOVE cid#1555013 COPY_INSTEAD_OF_MOVE cid#1555037 COPY_INSTEAD_OF_MOVE cid#1555050 COPY_INSTEAD_OF_MOVE cid#1555057 COPY_INSTEAD_OF_MOVE cid#1555066 COPY_INSTEAD_OF_MOVE cid#1555067 COPY_INSTEAD_OF_MOVE cid#1555083 COPY_INSTEAD_OF_MOVE cid#1555097 COPY_INSTEAD_OF_MOVE cid#1555135 COPY_INSTEAD_OF_MOVE cid#1555140 COPY_INSTEAD_OF_MOVE cid#1555146 COPY_INSTEAD_OF_MOVE cid#1555148 COPY_INSTEAD_OF_MOVE cid#1555149 COPY_INSTEAD_OF_MOVE cid#1555155 COPY_INSTEAD_OF_MOVE cid#1555157 COPY_INSTEAD_OF_MOVE cid#1555168 COPY_INSTEAD_OF_MOVE cid#1555195 COPY_INSTEAD_OF_MOVE cid#1555196 COPY_INSTEAD_OF_MOVE cid#1555237 COPY_INSTEAD_OF_MOVE Change-Id: I90531c19c28dca77fe99c72efdfc0972c311da98 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175377 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
2020 lines
74 KiB
C++
2020 lines
74 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <config_fuzzers.h>
|
|
|
|
#include <com/sun/star/beans/NamedValue.hpp>
|
|
#include <com/sun/star/beans/PropertyAttribute.hpp>
|
|
#include <com/sun/star/container/ElementExistException.hpp>
|
|
#include <com/sun/star/container/XEnumeration.hpp>
|
|
#include <com/sun/star/container/XNameContainer.hpp>
|
|
#include <com/sun/star/lang/XInitialization.hpp>
|
|
#include <com/sun/star/lang/XServiceInfo.hpp>
|
|
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
|
|
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
|
|
#include <com/sun/star/loader/XImplementationLoader.hpp>
|
|
#include <com/sun/star/registry/InvalidRegistryException.hpp>
|
|
#include <com/sun/star/uno/DeploymentException.hpp>
|
|
#include <com/sun/star/uno/Reference.hxx>
|
|
#include <com/sun/star/uno/XComponentContext.hpp>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <cppuhelper/bootstrap.hxx>
|
|
#include <cppuhelper/component_context.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <cppuhelper/factory.hxx>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <osl/file.hxx>
|
|
#include <osl/module.hxx>
|
|
#include <rtl/ref.hxx>
|
|
#include <rtl/uri.hxx>
|
|
#include <rtl/ustring.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <uno/environment.hxx>
|
|
#include <uno/mapping.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
|
|
#include "loadsharedlibcomponentfactory.hxx"
|
|
|
|
#include <registry/registry.hxx>
|
|
#include <xmlreader/xmlreader.hxx>
|
|
|
|
#include "paths.hxx"
|
|
#include "servicemanager.hxx"
|
|
|
|
namespace {
|
|
|
|
void insertImplementationMap(
|
|
cppuhelper::ServiceManager::Data::ImplementationMap * destination,
|
|
cppuhelper::ServiceManager::Data::ImplementationMap const & source)
|
|
{
|
|
assert(destination != nullptr);
|
|
for (const auto& [rName, rImpls] : source)
|
|
{
|
|
auto & impls = (*destination)[rName];
|
|
impls.insert(impls.end(), rImpls.begin(), rImpls.end());
|
|
}
|
|
}
|
|
|
|
void removeFromImplementationMap(
|
|
cppuhelper::ServiceManager::Data::ImplementationMap * map,
|
|
std::vector< OUString > const & elements,
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
|
|
const & implementation)
|
|
{
|
|
// The underlying data structures make this function somewhat inefficient,
|
|
// but the assumption is that it is rarely called:
|
|
assert(map != nullptr);
|
|
for (const auto& rElement : elements)
|
|
{
|
|
auto j(map->find(rElement));
|
|
assert(j != map->end());
|
|
auto k(std::find(j->second.begin(), j->second.end(), implementation));
|
|
assert(k != j->second.end());
|
|
j->second.erase(k);
|
|
if (j->second.empty()) {
|
|
map->erase(j);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For simplicity, this code keeps throwing
|
|
// css::registry::InvalidRegistryException for invalid XML rdbs (even though
|
|
// that does not fit the exception's name):
|
|
class Parser {
|
|
public:
|
|
Parser(
|
|
OUString const & uri,
|
|
css::uno::Reference< css::uno::XComponentContext > alienContext,
|
|
cppuhelper::ServiceManager::Data * data);
|
|
|
|
Parser(const Parser&) = delete;
|
|
const Parser& operator=(const Parser&) = delete;
|
|
|
|
private:
|
|
void handleComponent();
|
|
|
|
void handleImplementation();
|
|
|
|
void handleService();
|
|
|
|
void handleSingleton();
|
|
|
|
OUString getNameAttribute();
|
|
|
|
xmlreader::XmlReader reader_;
|
|
css::uno::Reference< css::uno::XComponentContext > alienContext_;
|
|
cppuhelper::ServiceManager::Data * data_;
|
|
OUString attrLoader_;
|
|
OUString attrUri_;
|
|
OUString attrEnvironment_;
|
|
OUString attrPrefix_;
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
|
|
implementation_;
|
|
};
|
|
|
|
Parser::Parser(
|
|
OUString const & uri,
|
|
css::uno::Reference< css::uno::XComponentContext > alienContext,
|
|
cppuhelper::ServiceManager::Data * data):
|
|
reader_(uri), alienContext_(std::move(alienContext)), data_(data)
|
|
{
|
|
assert(data != nullptr);
|
|
int ucNsId = reader_.registerNamespaceIri(
|
|
xmlreader::Span(
|
|
RTL_CONSTASCII_STRINGPARAM(
|
|
"http://openoffice.org/2010/uno-components")));
|
|
enum State {
|
|
STATE_BEGIN, STATE_END, STATE_COMPONENTS, STATE_COMPONENT_INITIAL,
|
|
STATE_COMPONENT, STATE_IMPLEMENTATION, STATE_SERVICE, STATE_SINGLETON };
|
|
for (State state = STATE_BEGIN;;) {
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
xmlreader::XmlReader::Result res = reader_.nextItem(
|
|
xmlreader::XmlReader::Text::NONE, &name, &nsId);
|
|
switch (state) {
|
|
case STATE_BEGIN:
|
|
if (res == xmlreader::XmlReader::Result::Begin && nsId == ucNsId
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("components")))
|
|
{
|
|
state = STATE_COMPONENTS;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in outer level");
|
|
case STATE_END:
|
|
if (res == xmlreader::XmlReader::Result::Done) {
|
|
return;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in outer level");
|
|
case STATE_COMPONENTS:
|
|
if (res == xmlreader::XmlReader::Result::End) {
|
|
state = STATE_END;
|
|
break;
|
|
}
|
|
if (res == xmlreader::XmlReader::Result::Begin && nsId == ucNsId
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("component")))
|
|
{
|
|
handleComponent();
|
|
state = STATE_COMPONENT_INITIAL;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in <components>");
|
|
case STATE_COMPONENT:
|
|
if (res == xmlreader::XmlReader::Result::End) {
|
|
state = STATE_COMPONENTS;
|
|
break;
|
|
}
|
|
[[fallthrough]];
|
|
case STATE_COMPONENT_INITIAL:
|
|
if (res == xmlreader::XmlReader::Result::Begin && nsId == ucNsId
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("implementation")))
|
|
{
|
|
handleImplementation();
|
|
state = STATE_IMPLEMENTATION;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in <component>");
|
|
case STATE_IMPLEMENTATION:
|
|
if (res == xmlreader::XmlReader::Result::End) {
|
|
state = STATE_COMPONENT;
|
|
break;
|
|
}
|
|
if (res == xmlreader::XmlReader::Result::Begin && nsId == ucNsId
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("service")))
|
|
{
|
|
handleService();
|
|
state = STATE_SERVICE;
|
|
break;
|
|
}
|
|
if (res == xmlreader::XmlReader::Result::Begin && nsId == ucNsId
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("singleton")))
|
|
{
|
|
handleSingleton();
|
|
state = STATE_SINGLETON;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in <implementation>");
|
|
case STATE_SERVICE:
|
|
if (res == xmlreader::XmlReader::Result::End) {
|
|
state = STATE_IMPLEMENTATION;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in <service>");
|
|
case STATE_SINGLETON:
|
|
if (res == xmlreader::XmlReader::Result::End) {
|
|
state = STATE_IMPLEMENTATION;
|
|
break;
|
|
}
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected item in <service>");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Parser::handleComponent() {
|
|
attrLoader_ = OUString();
|
|
attrUri_ = OUString();
|
|
attrEnvironment_ = OUString();
|
|
attrPrefix_ = OUString();
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
while (reader_.nextAttribute(&nsId, &name)) {
|
|
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("loader")))
|
|
{
|
|
if (!attrLoader_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <component> has multiple \"loader\" attributes");
|
|
}
|
|
attrLoader_ = reader_.getAttributeValue(false).convertFromUtf8();
|
|
if (attrLoader_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <component> has empty \"loader\" attribute");
|
|
}
|
|
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("uri")))
|
|
{
|
|
if (!attrUri_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <component> has multiple \"uri\" attributes");
|
|
}
|
|
attrUri_ = reader_.getAttributeValue(false).convertFromUtf8();
|
|
if (attrUri_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <component> has empty \"uri\" attribute");
|
|
}
|
|
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("environment")))
|
|
{
|
|
if (!attrEnvironment_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() +
|
|
": <component> has multiple \"environment\" attributes");
|
|
}
|
|
attrEnvironment_ = reader_.getAttributeValue(false)
|
|
.convertFromUtf8();
|
|
if (attrEnvironment_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() +
|
|
": <component> has empty \"environment\" attribute");
|
|
}
|
|
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("prefix")))
|
|
{
|
|
if (!attrPrefix_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() +
|
|
": <component> has multiple \"prefix\" attributes");
|
|
}
|
|
attrPrefix_ = reader_.getAttributeValue(false).convertFromUtf8();
|
|
if (attrPrefix_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() +
|
|
": <component> has empty \"prefix\" attribute");
|
|
}
|
|
} else {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected attribute \""
|
|
+ name.convertFromUtf8() + "\" in <component>");
|
|
}
|
|
}
|
|
if (attrLoader_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": <component> is missing \"loader\" attribute");
|
|
}
|
|
if (attrUri_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": <component> is missing \"uri\" attribute");
|
|
}
|
|
#ifndef DISABLE_DYNLOADING
|
|
try {
|
|
attrUri_ = rtl::Uri::convertRelToAbs(reader_.getUrl(), attrUri_);
|
|
} catch (const rtl::MalformedUriException & e) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": bad \"uri\" attribute: " + e.getMessage());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Parser::handleImplementation() {
|
|
OUString attrName;
|
|
OUString attrConstructor;
|
|
bool attrSingleInstance = false;
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
while (reader_.nextAttribute(&nsId, &name)) {
|
|
if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("name")))
|
|
{
|
|
if (!attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> has multiple \"name\" attributes");
|
|
}
|
|
attrName = reader_.getAttributeValue(false).convertFromUtf8();
|
|
if (attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> has empty \"name\" attribute");
|
|
}
|
|
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("constructor")))
|
|
{
|
|
if (!attrConstructor.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> has multiple \"constructor\""
|
|
" attributes");
|
|
}
|
|
attrConstructor = reader_.getAttributeValue(false)
|
|
.convertFromUtf8();
|
|
if (attrConstructor.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": element has empty \"constructor\" attribute");
|
|
}
|
|
if (attrEnvironment_.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> has \"constructor\" attribute but"
|
|
" <component> has no \"environment\" attribute");
|
|
}
|
|
} else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
|
|
&& name.equals(RTL_CONSTASCII_STRINGPARAM("single-instance")))
|
|
{
|
|
if (attrSingleInstance) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> has multiple \"single-instance\" attributes");
|
|
}
|
|
if (!reader_.getAttributeValue(false).equals(RTL_CONSTASCII_STRINGPARAM("true"))) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": <implementation> has bad \"single-instance\" attribute");
|
|
}
|
|
attrSingleInstance = true;
|
|
} else {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": unexpected element attribute \""
|
|
+ name.convertFromUtf8() + "\" in <implementation>");
|
|
}
|
|
}
|
|
if (attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": <implementation> is missing \"name\" attribute");
|
|
}
|
|
implementation_ =
|
|
std::make_shared<cppuhelper::ServiceManager::Data::Implementation>(
|
|
attrName, attrLoader_, attrUri_, attrEnvironment_, attrConstructor,
|
|
attrPrefix_, attrSingleInstance, alienContext_, reader_.getUrl());
|
|
if (!data_->namedImplementations.emplace(attrName, implementation_).
|
|
second)
|
|
{
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": duplicate <implementation name=\"" + attrName
|
|
+ "\">");
|
|
}
|
|
}
|
|
|
|
void Parser::handleService() {
|
|
OUString name(getNameAttribute());
|
|
implementation_->services.push_back(name);
|
|
data_->services[name].push_back(implementation_);
|
|
}
|
|
|
|
void Parser::handleSingleton() {
|
|
OUString name(getNameAttribute());
|
|
implementation_->singletons.push_back(name);
|
|
data_->singletons[name].push_back(implementation_);
|
|
}
|
|
|
|
OUString Parser::getNameAttribute() {
|
|
OUString attrName;
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
while (reader_.nextAttribute(&nsId, &name)) {
|
|
if (nsId != xmlreader::XmlReader::NAMESPACE_NONE
|
|
|| !name.equals(RTL_CONSTASCII_STRINGPARAM("name")))
|
|
{
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": expected element attribute \"name\"");
|
|
}
|
|
if (!attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl()
|
|
+ ": element has multiple \"name\" attributes");
|
|
}
|
|
attrName = reader_.getAttributeValue(false).convertFromUtf8();
|
|
if (attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": element has empty \"name\" attribute");
|
|
}
|
|
}
|
|
if (attrName.isEmpty()) {
|
|
throw css::registry::InvalidRegistryException(
|
|
reader_.getUrl() + ": element is missing \"name\" attribute");
|
|
}
|
|
return attrName;
|
|
}
|
|
|
|
class ContentEnumeration:
|
|
public cppu::WeakImplHelper< css::container::XEnumeration >
|
|
{
|
|
public:
|
|
explicit ContentEnumeration(std::vector< css::uno::Any >&& factories):
|
|
factories_(std::move(factories)), iterator_(factories_.begin()) {}
|
|
|
|
ContentEnumeration(const ContentEnumeration&) = delete;
|
|
const ContentEnumeration& operator=(const ContentEnumeration&) = delete;
|
|
|
|
private:
|
|
virtual ~ContentEnumeration() override {}
|
|
|
|
virtual sal_Bool SAL_CALL hasMoreElements() override;
|
|
|
|
virtual css::uno::Any SAL_CALL nextElement() override;
|
|
|
|
std::mutex mutex_;
|
|
std::vector< css::uno::Any > factories_;
|
|
std::vector< css::uno::Any >::const_iterator iterator_;
|
|
};
|
|
|
|
sal_Bool ContentEnumeration::hasMoreElements()
|
|
{
|
|
std::scoped_lock g(mutex_);
|
|
return iterator_ != factories_.end();
|
|
}
|
|
|
|
css::uno::Any ContentEnumeration::nextElement()
|
|
{
|
|
std::scoped_lock g(mutex_);
|
|
if (iterator_ == factories_.end()) {
|
|
throw css::container::NoSuchElementException(
|
|
u"Bootstrap service manager service enumerator has no more elements"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return *iterator_++;
|
|
}
|
|
|
|
css::beans::Property getDefaultContextProperty() {
|
|
return css::beans::Property(
|
|
u"DefaultContext"_ustr, -1,
|
|
cppu::UnoType< css::uno::XComponentContext >::get(),
|
|
css::beans::PropertyAttribute::READONLY);
|
|
}
|
|
|
|
class SingletonFactory:
|
|
public cppu::WeakImplHelper<css::lang::XSingleComponentFactory>
|
|
{
|
|
public:
|
|
SingletonFactory(
|
|
rtl::Reference< cppuhelper::ServiceManager > const & manager,
|
|
std::shared_ptr<
|
|
cppuhelper::ServiceManager::Data::Implementation > const &
|
|
implementation):
|
|
manager_(manager), implementation_(implementation)
|
|
{ assert(manager.is()); assert(implementation); }
|
|
|
|
SingletonFactory(const SingletonFactory&) = delete;
|
|
const SingletonFactory& operator=(const SingletonFactory&) = delete;
|
|
|
|
private:
|
|
virtual ~SingletonFactory() override {}
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstanceWithContext(
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context) override;
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstanceWithArgumentsAndContext(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context) override;
|
|
|
|
rtl::Reference< cppuhelper::ServiceManager > manager_;
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
|
|
implementation_;
|
|
};
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
SingletonFactory::createInstanceWithContext(
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
manager_->loadImplementation(Context, implementation_);
|
|
return implementation_->createInstance(Context, true);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
SingletonFactory::createInstanceWithArgumentsAndContext(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
manager_->loadImplementation(Context, implementation_);
|
|
return implementation_->createInstanceWithArguments(
|
|
Context, true, Arguments);
|
|
}
|
|
|
|
class ImplementationWrapper:
|
|
public cppu::WeakImplHelper<
|
|
css::lang::XSingleComponentFactory, css::lang::XSingleServiceFactory,
|
|
css::lang::XServiceInfo >
|
|
{
|
|
public:
|
|
ImplementationWrapper(
|
|
rtl::Reference< cppuhelper::ServiceManager > const & manager,
|
|
std::shared_ptr<
|
|
cppuhelper::ServiceManager::Data::Implementation > const &
|
|
implementation):
|
|
manager_(manager), implementation_(implementation)
|
|
{ assert(manager.is()); assert(implementation); }
|
|
|
|
ImplementationWrapper(const ImplementationWrapper&) = delete;
|
|
const ImplementationWrapper& operator=(const ImplementationWrapper&) = delete;
|
|
|
|
private:
|
|
virtual ~ImplementationWrapper() override {}
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstanceWithContext(
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context) override;
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstanceWithArgumentsAndContext(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context) override;
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstance() override;
|
|
|
|
virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
|
|
createInstanceWithArguments(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments) override;
|
|
|
|
virtual OUString SAL_CALL getImplementationName() override;
|
|
|
|
virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
|
|
|
|
virtual css::uno::Sequence< OUString > SAL_CALL
|
|
getSupportedServiceNames() override;
|
|
|
|
rtl::Reference< cppuhelper::ServiceManager > manager_;
|
|
std::weak_ptr< cppuhelper::ServiceManager::Data::Implementation >
|
|
implementation_;
|
|
};
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
ImplementationWrapper::createInstanceWithContext(
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
|
|
assert(impl);
|
|
manager_->loadImplementation(Context, impl);
|
|
return impl->createInstance(Context, false);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
ImplementationWrapper::createInstanceWithArgumentsAndContext(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
|
|
assert(impl);
|
|
manager_->loadImplementation(Context, impl);
|
|
return impl->createInstanceWithArguments(
|
|
Context, false, Arguments);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
ImplementationWrapper::createInstance()
|
|
{
|
|
return createInstanceWithContext(manager_->getContext());
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
ImplementationWrapper::createInstanceWithArguments(
|
|
css::uno::Sequence< css::uno::Any > const & Arguments)
|
|
{
|
|
return createInstanceWithArgumentsAndContext(
|
|
Arguments, manager_->getContext());
|
|
}
|
|
|
|
OUString ImplementationWrapper::getImplementationName()
|
|
{
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
|
|
assert(impl);
|
|
return impl->name;
|
|
}
|
|
|
|
sal_Bool ImplementationWrapper::supportsService(OUString const & ServiceName)
|
|
{
|
|
return cppu::supportsService(this, ServiceName);
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
ImplementationWrapper::getSupportedServiceNames()
|
|
{
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
|
|
assert(impl);
|
|
if (impl->services.size()
|
|
> o3tl::make_unsigned(SAL_MAX_INT32))
|
|
{
|
|
throw css::uno::RuntimeException(
|
|
("Implementation " + impl->name
|
|
+ " supports too many services"),
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return comphelper::containerToSequence(impl->services);
|
|
}
|
|
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface>
|
|
cppuhelper::ServiceManager::Data::Implementation::createInstance(
|
|
css::uno::Reference<css::uno::XComponentContext> const & context,
|
|
bool singletonRequest)
|
|
{
|
|
css::uno::Reference<css::uno::XInterface> inst;
|
|
if (isSingleInstance) {
|
|
std::unique_lock g(mutex);
|
|
if (!singleInstance.is()) {
|
|
singleInstance = doCreateInstance(context);
|
|
}
|
|
inst = singleInstance;
|
|
} else {
|
|
inst = doCreateInstance(context);
|
|
}
|
|
updateDisposeInstance(singletonRequest, inst);
|
|
return inst;
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface>
|
|
cppuhelper::ServiceManager::Data::Implementation::createInstanceWithArguments(
|
|
css::uno::Reference<css::uno::XComponentContext> const & context,
|
|
bool singletonRequest, css::uno::Sequence<css::uno::Any> const & arguments)
|
|
{
|
|
css::uno::Reference<css::uno::XInterface> inst;
|
|
if (isSingleInstance) {
|
|
std::unique_lock g(mutex);
|
|
if (!singleInstance.is()) {
|
|
singleInstance = doCreateInstanceWithArguments(context, arguments);
|
|
}
|
|
inst = singleInstance;
|
|
} else {
|
|
inst = doCreateInstanceWithArguments(context, arguments);
|
|
}
|
|
updateDisposeInstance(singletonRequest, inst);
|
|
return inst;
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface>
|
|
cppuhelper::ServiceManager::Data::Implementation::doCreateInstance(
|
|
css::uno::Reference<css::uno::XComponentContext> const & context)
|
|
{
|
|
if (constructorFn) {
|
|
return css::uno::Reference<css::uno::XInterface>(
|
|
constructorFn(context.get(), css::uno::Sequence<css::uno::Any>()),
|
|
SAL_NO_ACQUIRE);
|
|
} else if (factory1.is()) {
|
|
return factory1->createInstanceWithContext(context);
|
|
} else {
|
|
assert(factory2.is());
|
|
return factory2->createInstance();
|
|
}
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface>
|
|
cppuhelper::ServiceManager::Data::Implementation::doCreateInstanceWithArguments(
|
|
css::uno::Reference<css::uno::XComponentContext> const & context,
|
|
css::uno::Sequence<css::uno::Any> const & arguments)
|
|
{
|
|
if (constructorFn) {
|
|
css::uno::Reference<css::uno::XInterface> inst(
|
|
constructorFn(context.get(), arguments), SAL_NO_ACQUIRE);
|
|
//HACK: The constructor will either observe arguments and return inst
|
|
// that does not implement XInitialization (or null), or ignore
|
|
// arguments and return inst that implements XInitialization; this
|
|
// should be removed again once XInitialization-based implementations
|
|
// have become rare:
|
|
css::uno::Reference<css::lang::XInitialization> init(
|
|
inst, css::uno::UNO_QUERY);
|
|
if (init.is()) {
|
|
init->initialize(arguments);
|
|
}
|
|
return inst;
|
|
} else if (factory1.is()) {
|
|
return factory1->createInstanceWithArgumentsAndContext(
|
|
arguments, context);
|
|
} else {
|
|
assert(factory2.is());
|
|
return factory2->createInstanceWithArguments(arguments);
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::Data::Implementation::updateDisposeInstance(
|
|
bool singletonRequest,
|
|
css::uno::Reference<css::uno::XInterface> const & instance)
|
|
{
|
|
// This is an optimization, to only call dispose once (from the component
|
|
// context) on a singleton that is obtained both via the component context
|
|
// and via the service manager; however, there is a harmless race here that
|
|
// may cause two calls to dispose nevertheless (also, this calls dispose on
|
|
// at most one of the instances obtained via the service manager, in case
|
|
// the implementation hands out different instances):
|
|
if (singletonRequest) {
|
|
std::unique_lock g(mutex);
|
|
disposeInstance.clear();
|
|
dispose = false;
|
|
} else if (shallDispose()) {
|
|
css::uno::Reference<css::lang::XComponent> comp(
|
|
instance, css::uno::UNO_QUERY);
|
|
if (comp.is()) {
|
|
std::unique_lock g(mutex);
|
|
if (dispose) {
|
|
disposeInstance = comp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::addSingletonContextEntries(
|
|
std::vector< cppu::ContextEntry_Init > * entries)
|
|
{
|
|
assert(entries != nullptr);
|
|
for (const auto& [rName, rImpls] : data_.singletons)
|
|
{
|
|
assert(!rImpls.empty());
|
|
assert(rImpls[0]);
|
|
SAL_INFO_IF(
|
|
rImpls.size() > 1, "cppuhelper",
|
|
"Arbitrarily choosing " << rImpls[0]->name
|
|
<< " among multiple implementations for " << rName);
|
|
entries->push_back(
|
|
cppu::ContextEntry_Init(
|
|
"/singletons/" + rName,
|
|
css::uno::Any(
|
|
css::uno::Reference<css::lang::XSingleComponentFactory>(
|
|
new SingletonFactory(this, rImpls[0]))),
|
|
true));
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::loadImplementation(
|
|
css::uno::Reference< css::uno::XComponentContext > const & context,
|
|
std::shared_ptr< Data::Implementation > const & implementation)
|
|
{
|
|
assert(implementation);
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
if (implementation->status == Data::Implementation::STATUS_LOADED) {
|
|
return;
|
|
}
|
|
}
|
|
OUString uri;
|
|
try {
|
|
uri = cppu::bootstrap_expandUri(implementation->uri);
|
|
} catch (css::lang::IllegalArgumentException & e) {
|
|
throw css::uno::DeploymentException(
|
|
"Cannot expand URI" + implementation->uri + ": " + e.Message,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
cppuhelper::WrapperConstructorFn ctor;
|
|
css::uno::Reference< css::uno::XInterface > f0;
|
|
// Special handling of SharedLibrary loader, with support for environment,
|
|
// constructor, and prefix arguments:
|
|
if (!implementation->alienContext.is()
|
|
&& implementation->loader == "com.sun.star.loader.SharedLibrary")
|
|
{
|
|
cppuhelper::detail::loadSharedLibComponentFactory(
|
|
uri, implementation->environment,
|
|
implementation->prefix, implementation->name,
|
|
implementation->constructorName, this, &ctor, &f0);
|
|
if (ctor) {
|
|
assert(!implementation->environment.isEmpty());
|
|
}
|
|
} else {
|
|
SAL_WARN_IF(
|
|
!implementation->environment.isEmpty(), "cppuhelper",
|
|
"Loader " << implementation->loader
|
|
<< " and non-empty environment "
|
|
<< implementation->environment);
|
|
SAL_WARN_IF(
|
|
!implementation->prefix.isEmpty(), "cppuhelper",
|
|
"Loader " << implementation->loader
|
|
<< " and non-empty constructor "
|
|
<< implementation->constructorName);
|
|
SAL_WARN_IF(
|
|
!implementation->prefix.isEmpty(), "cppuhelper",
|
|
"Loader " << implementation->loader
|
|
<< " and non-empty prefix " << implementation->prefix);
|
|
css::uno::Reference< css::uno::XComponentContext > ctxt;
|
|
css::uno::Reference< css::lang::XMultiComponentFactory > smgr;
|
|
if (implementation->alienContext.is()) {
|
|
ctxt = implementation->alienContext;
|
|
smgr.set(ctxt->getServiceManager(), css::uno::UNO_SET_THROW);
|
|
} else {
|
|
assert(context.is());
|
|
ctxt = context;
|
|
smgr = this;
|
|
}
|
|
css::uno::Reference< css::loader::XImplementationLoader > loader(
|
|
smgr->createInstanceWithContext(implementation->loader, ctxt),
|
|
css::uno::UNO_QUERY_THROW);
|
|
f0 = loader->activate(
|
|
implementation->name, OUString(), uri,
|
|
css::uno::Reference< css::registry::XRegistryKey >());
|
|
}
|
|
css::uno::Reference<css::lang::XSingleComponentFactory> f1;
|
|
css::uno::Reference<css::lang::XSingleServiceFactory> f2;
|
|
if (!ctor) {
|
|
f1.set(f0, css::uno::UNO_QUERY);
|
|
if (!f1.is()) {
|
|
f2.set(f0, css::uno::UNO_QUERY);
|
|
if (!f2.is()) {
|
|
throw css::uno::DeploymentException(
|
|
("Implementation " + implementation->name
|
|
+ " does not provide a constructor or factory"),
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
}
|
|
}
|
|
//TODO: There is a race here, as the relevant service factory can be removed
|
|
// while the mutex is unlocked and loading can thus fail, as the entity from
|
|
// which to load can disappear once the service factory is removed.
|
|
std::unique_lock g(m_aMutex);
|
|
if (!(m_bDisposed
|
|
|| implementation->status == Data::Implementation::STATUS_LOADED))
|
|
{
|
|
implementation->status = Data::Implementation::STATUS_LOADED;
|
|
implementation->constructorFn = std::move(ctor);
|
|
implementation->factory1 = f1;
|
|
implementation->factory2 = f2;
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::disposing(std::unique_lock<std::mutex>& rGuard) {
|
|
std::vector< css::uno::Reference<css::lang::XComponent> > sngls;
|
|
std::vector< css::uno::Reference< css::lang::XComponent > > comps;
|
|
Data clear;
|
|
{
|
|
for (const auto& rEntry : data_.namedImplementations)
|
|
{
|
|
assert(rEntry.second);
|
|
if (rEntry.second->shallDispose()) {
|
|
std::unique_lock g2(rEntry.second->mutex);
|
|
if (rEntry.second->disposeInstance.is()) {
|
|
sngls.push_back(rEntry.second->disposeInstance);
|
|
}
|
|
}
|
|
}
|
|
for (const auto& rEntry : data_.dynamicImplementations)
|
|
{
|
|
assert(rEntry.second);
|
|
if (rEntry.second->shallDispose()) {
|
|
std::unique_lock g2(rEntry.second->mutex);
|
|
if (rEntry.second->disposeInstance.is()) {
|
|
sngls.push_back(rEntry.second->disposeInstance);
|
|
}
|
|
}
|
|
if (rEntry.second->component.is()) {
|
|
comps.push_back(rEntry.second->component);
|
|
}
|
|
}
|
|
data_.namedImplementations.swap(clear.namedImplementations);
|
|
data_.dynamicImplementations.swap(clear.dynamicImplementations);
|
|
data_.services.swap(clear.services);
|
|
data_.singletons.swap(clear.singletons);
|
|
}
|
|
rGuard.unlock();
|
|
for (const auto& rxSngl : sngls)
|
|
{
|
|
try {
|
|
rxSngl->dispose();
|
|
} catch (css::uno::RuntimeException & e) {
|
|
SAL_WARN("cppuhelper", "Ignoring " << e << " while disposing singleton");
|
|
}
|
|
}
|
|
for (const auto& rxComp : comps)
|
|
{
|
|
removeEventListenerFromComponent(rxComp);
|
|
}
|
|
rGuard.lock();
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::initialize(
|
|
css::uno::Sequence<css::uno::Any> const & aArguments)
|
|
{
|
|
OUString arg;
|
|
if (aArguments.getLength() != 1 || !(aArguments[0] >>= arg)
|
|
|| arg != "preload")
|
|
{
|
|
throw css::lang::IllegalArgumentException(
|
|
u"invalid ServiceManager::initialize argument"_ustr,
|
|
css::uno::Reference<css::uno::XInterface>(), 0);
|
|
}
|
|
preloadImplementations();
|
|
}
|
|
|
|
OUString cppuhelper::ServiceManager::getImplementationName()
|
|
{
|
|
return
|
|
u"com.sun.star.comp.cppuhelper.bootstrap.ServiceManager"_ustr;
|
|
}
|
|
|
|
sal_Bool cppuhelper::ServiceManager::supportsService(
|
|
OUString const & ServiceName)
|
|
{
|
|
return cppu::supportsService(this, ServiceName);
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
cppuhelper::ServiceManager::getSupportedServiceNames()
|
|
{
|
|
return { u"com.sun.star.lang.MultiServiceFactory"_ustr, u"com.sun.star.lang.ServiceManager"_ustr };
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
cppuhelper::ServiceManager::createInstance(
|
|
OUString const & aServiceSpecifier)
|
|
{
|
|
assert(context_.is());
|
|
return createInstanceWithContext(aServiceSpecifier, context_);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
cppuhelper::ServiceManager::createInstanceWithArguments(
|
|
OUString const & ServiceSpecifier,
|
|
css::uno::Sequence< css::uno::Any > const & Arguments)
|
|
{
|
|
assert(context_.is());
|
|
return createInstanceWithArgumentsAndContext(
|
|
ServiceSpecifier, Arguments, context_);
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
cppuhelper::ServiceManager::getAvailableServiceNames()
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
if (m_bDisposed) {
|
|
return css::uno::Sequence< OUString >();
|
|
}
|
|
if (data_.services.size() > o3tl::make_unsigned(SAL_MAX_INT32)) {
|
|
throw css::uno::RuntimeException(
|
|
u"getAvailableServiceNames: too many services"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return comphelper::mapKeysToSequence(data_.services);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
cppuhelper::ServiceManager::createInstanceWithContext(
|
|
OUString const & aServiceSpecifier,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
std::shared_ptr< Data::Implementation > impl(
|
|
findServiceImplementation(Context, aServiceSpecifier));
|
|
return impl == nullptr ? css::uno::Reference<css::uno::XInterface>()
|
|
: impl->createInstance(Context, false);
|
|
}
|
|
|
|
css::uno::Reference< css::uno::XInterface >
|
|
cppuhelper::ServiceManager::createInstanceWithArgumentsAndContext(
|
|
OUString const & ServiceSpecifier,
|
|
css::uno::Sequence< css::uno::Any > const & Arguments,
|
|
css::uno::Reference< css::uno::XComponentContext > const & Context)
|
|
{
|
|
std::shared_ptr< Data::Implementation > impl(
|
|
findServiceImplementation(Context, ServiceSpecifier));
|
|
return impl == nullptr ? css::uno::Reference<css::uno::XInterface>()
|
|
: impl->createInstanceWithArguments(Context, false, Arguments);
|
|
}
|
|
|
|
css::uno::Type cppuhelper::ServiceManager::getElementType()
|
|
{
|
|
return css::uno::Type();
|
|
}
|
|
|
|
sal_Bool cppuhelper::ServiceManager::hasElements()
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
return
|
|
!(data_.namedImplementations.empty()
|
|
&& data_.dynamicImplementations.empty());
|
|
}
|
|
|
|
css::uno::Reference< css::container::XEnumeration >
|
|
cppuhelper::ServiceManager::createEnumeration()
|
|
{
|
|
throw css::uno::RuntimeException(
|
|
u"ServiceManager createEnumeration: method not supported"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
|
|
sal_Bool cppuhelper::ServiceManager::has(css::uno::Any const &)
|
|
{
|
|
throw css::uno::RuntimeException(
|
|
u"ServiceManager has: method not supported"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::insert(css::uno::Any const & aElement)
|
|
{
|
|
css::uno::Sequence< css::beans::NamedValue > args;
|
|
if (aElement >>= args) {
|
|
std::vector< OUString > uris;
|
|
css::uno::Reference< css::uno::XComponentContext > alienContext;
|
|
for (const auto & arg : args) {
|
|
if (arg.Name == "uri") {
|
|
OUString uri;
|
|
if (!(arg.Value >>= uri)) {
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Bad uri argument"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
uris.push_back(uri);
|
|
} else if (arg.Name == "component-context") {
|
|
if (alienContext.is()) {
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Multiple component-context arguments"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
if (!(arg.Value >>= alienContext) || !alienContext.is()) {
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Bad component-context argument"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
} else {
|
|
throw css::lang::IllegalArgumentException(
|
|
"Bad argument " + arg.Name,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
}
|
|
insertRdbFiles(uris, alienContext);
|
|
return;
|
|
}
|
|
css::uno::Reference< css::lang::XServiceInfo > info;
|
|
if ((aElement >>= info) && info.is()) {
|
|
insertLegacyFactory(info);
|
|
return;
|
|
}
|
|
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Bad insert element"_ustr, static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::remove(css::uno::Any const & aElement)
|
|
{
|
|
css::uno::Sequence< css::beans::NamedValue > args;
|
|
if (aElement >>= args) {
|
|
std::vector< OUString > uris;
|
|
for (const auto & i : args) {
|
|
if (i.Name != "uri") {
|
|
throw css::lang::IllegalArgumentException(
|
|
"Bad argument " + i.Name,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
OUString uri;
|
|
if (!(i.Value >>= uri)) {
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Bad uri argument"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
uris.push_back(uri);
|
|
}
|
|
removeRdbFiles(uris);
|
|
return;
|
|
}
|
|
css::uno::Reference< css::lang::XServiceInfo > info;
|
|
if ((aElement >>= info) && info.is()) {
|
|
if (!removeLegacyFactory(info, true)) {
|
|
throw css::container::NoSuchElementException(
|
|
u"Remove non-inserted factory object"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return;
|
|
}
|
|
OUString impl;
|
|
if (aElement >>= impl) {
|
|
// For live-removal of extensions:
|
|
removeImplementation(impl);
|
|
return;
|
|
}
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Bad remove element"_ustr, static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
|
|
css::uno::Reference< css::container::XEnumeration >
|
|
cppuhelper::ServiceManager::createContentEnumeration(
|
|
OUString const & aServiceName)
|
|
{
|
|
boost::container::small_vector< std::shared_ptr< Data::Implementation >, 2 > impls;
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
Data::ImplementationMap::const_iterator i(
|
|
data_.services.find(aServiceName));
|
|
if (i != data_.services.end()) {
|
|
impls = i->second;
|
|
}
|
|
}
|
|
std::vector< css::uno::Any > factories;
|
|
for (const auto& rxImpl : impls)
|
|
{
|
|
Data::Implementation * impl = rxImpl.get();
|
|
assert(impl != nullptr);
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
if (m_bDisposed) {
|
|
factories.clear();
|
|
break;
|
|
}
|
|
if (impl->status == Data::Implementation::STATUS_NEW) {
|
|
// Postpone actual implementation instantiation as long as
|
|
// possible (so that e.g. opening LO's "Tools - Macros" menu
|
|
// does not try to instantiate a JVM, which can lead to a
|
|
// synchronous error dialog when no JVM is specified, and
|
|
// showing the dialog while hovering over a menu can cause
|
|
// trouble):
|
|
impl->factory1 = new ImplementationWrapper(this, rxImpl);
|
|
impl->status = Data::Implementation::STATUS_WRAPPER;
|
|
}
|
|
if (impl->constructorFn != nullptr && !impl->factory1.is()) {
|
|
impl->factory1 = new ImplementationWrapper(this, rxImpl);
|
|
}
|
|
}
|
|
if (impl->factory1.is()) {
|
|
factories.push_back(css::uno::Any(impl->factory1));
|
|
} else {
|
|
assert(impl->factory2.is());
|
|
factories.push_back(css::uno::Any(impl->factory2));
|
|
}
|
|
}
|
|
return new ContentEnumeration(std::move(factories));
|
|
}
|
|
|
|
css::uno::Reference< css::beans::XPropertySetInfo >
|
|
cppuhelper::ServiceManager::getPropertySetInfo()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::setPropertyValue(
|
|
OUString const & aPropertyName, css::uno::Any const &)
|
|
{
|
|
if (aPropertyName == "DefaultContext") {
|
|
throw css::beans::PropertyVetoException(
|
|
aPropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
} else {
|
|
throw css::beans::UnknownPropertyException(
|
|
aPropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
}
|
|
|
|
css::uno::Any cppuhelper::ServiceManager::getPropertyValue(
|
|
OUString const & PropertyName)
|
|
{
|
|
if (PropertyName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
PropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
assert(context_.is());
|
|
return css::uno::Any(context_);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::addPropertyChangeListener(
|
|
OUString const & aPropertyName,
|
|
css::uno::Reference< css::beans::XPropertyChangeListener > const &
|
|
xListener)
|
|
{
|
|
if (!aPropertyName.isEmpty() && aPropertyName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
aPropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
// DefaultContext does not change, so just treat it as an event listener:
|
|
return addEventListener(xListener);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::removePropertyChangeListener(
|
|
OUString const & aPropertyName,
|
|
css::uno::Reference< css::beans::XPropertyChangeListener > const &
|
|
aListener)
|
|
{
|
|
if (!aPropertyName.isEmpty() && aPropertyName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
aPropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
// DefaultContext does not change, so just treat it as an event listener:
|
|
return removeEventListener(aListener);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::addVetoableChangeListener(
|
|
OUString const & PropertyName,
|
|
css::uno::Reference< css::beans::XVetoableChangeListener > const &
|
|
aListener)
|
|
{
|
|
if (!PropertyName.isEmpty() && PropertyName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
PropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
// DefaultContext does not change, so just treat it as an event listener:
|
|
return addEventListener(aListener);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::removeVetoableChangeListener(
|
|
OUString const & PropertyName,
|
|
css::uno::Reference< css::beans::XVetoableChangeListener > const &
|
|
aListener)
|
|
{
|
|
if (!PropertyName.isEmpty() && PropertyName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
PropertyName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
// DefaultContext does not change, so just treat it as an event listener:
|
|
return removeEventListener(aListener);
|
|
}
|
|
|
|
css::uno::Sequence< css::beans::Property >
|
|
cppuhelper::ServiceManager::getProperties() {
|
|
return { getDefaultContextProperty() };
|
|
}
|
|
|
|
css::beans::Property cppuhelper::ServiceManager::getPropertyByName(
|
|
OUString const & aName)
|
|
{
|
|
if (aName != "DefaultContext") {
|
|
throw css::beans::UnknownPropertyException(
|
|
aName, static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return getDefaultContextProperty();
|
|
}
|
|
|
|
sal_Bool cppuhelper::ServiceManager::hasPropertyByName(
|
|
OUString const & Name)
|
|
{
|
|
return Name == "DefaultContext";
|
|
}
|
|
|
|
cppuhelper::ServiceManager::~ServiceManager() {}
|
|
|
|
void cppuhelper::ServiceManager::disposing(
|
|
css::lang::EventObject const & Source)
|
|
{
|
|
removeLegacyFactory(
|
|
css::uno::Reference< css::lang::XServiceInfo >(
|
|
Source.Source, css::uno::UNO_QUERY_THROW),
|
|
false);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::removeEventListenerFromComponent(
|
|
css::uno::Reference< css::lang::XComponent > const & component)
|
|
{
|
|
assert(component.is());
|
|
try {
|
|
component->removeEventListener(this);
|
|
} catch (css::uno::RuntimeException & e) {
|
|
SAL_INFO(
|
|
"cppuhelper",
|
|
"Ignored removeEventListener RuntimeException " + e.Message);
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::init(std::u16string_view rdbUris) {
|
|
for (sal_Int32 i = 0; i != -1;) {
|
|
std::u16string_view uri(o3tl::getToken(rdbUris, 0, ' ', i));
|
|
if (uri.empty()) {
|
|
continue;
|
|
}
|
|
bool optional;
|
|
bool directory;
|
|
cppu::decodeRdbUri(&uri, &optional, &directory);
|
|
if (directory) {
|
|
readRdbDirectory(uri, optional);
|
|
} else {
|
|
readRdbFile(OUString(uri), optional);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::readRdbDirectory(
|
|
std::u16string_view uri, bool optional)
|
|
{
|
|
osl::Directory dir = OUString(uri);
|
|
switch (dir.open()) {
|
|
case osl::FileBase::E_None:
|
|
break;
|
|
case osl::FileBase::E_NOENT:
|
|
if (optional) {
|
|
SAL_INFO("cppuhelper", "Ignored optional " << OUString(uri));
|
|
return;
|
|
}
|
|
[[fallthrough]];
|
|
default:
|
|
throw css::uno::DeploymentException(
|
|
OUString::Concat("Cannot open directory ") + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
for (;;) {
|
|
OUString url;
|
|
if (!cppu::nextDirectoryItem(dir, &url)) {
|
|
break;
|
|
}
|
|
readRdbFile(url, false);
|
|
}
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::readRdbFile(
|
|
OUString const & uri, bool optional)
|
|
{
|
|
try {
|
|
Parser(
|
|
uri, css::uno::Reference< css::uno::XComponentContext >(), &data_);
|
|
} catch (css::container::NoSuchElementException &) {
|
|
if (!optional) {
|
|
throw css::uno::DeploymentException(
|
|
uri + ": no such file",
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
SAL_INFO("cppuhelper", "Ignored optional " << uri);
|
|
}
|
|
#if !ENABLE_FUZZERS
|
|
catch (css::registry::InvalidRegistryException & e) {
|
|
if (!readLegacyRdbFile(uri)) {
|
|
throw css::uno::DeploymentException(
|
|
"InvalidRegistryException: " + e.Message,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
} catch (css::uno::RuntimeException &) {
|
|
if (!readLegacyRdbFile(uri)) {
|
|
throw;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !ENABLE_FUZZERS
|
|
bool cppuhelper::ServiceManager::readLegacyRdbFile(OUString const & uri) {
|
|
Registry reg;
|
|
switch (reg.open(uri, RegAccessMode::READONLY)) {
|
|
case RegError::NO_ERROR:
|
|
break;
|
|
case RegError::REGISTRY_NOT_EXISTS:
|
|
case RegError::INVALID_REGISTRY:
|
|
{
|
|
// Ignore empty rdb files (which are at least seen by subordinate
|
|
// uno processes during extension registration; Registry::open can
|
|
// fail on them if mmap(2) returns EINVAL for a zero length):
|
|
osl::DirectoryItem item;
|
|
if (osl::DirectoryItem::get(uri, item) == osl::FileBase::E_None) {
|
|
osl::FileStatus status(osl_FileStatus_Mask_FileSize);
|
|
if (item.getFileStatus(status) == osl::FileBase::E_None
|
|
&& status.getFileSize() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
default:
|
|
return false;
|
|
}
|
|
RegistryKey rootKey;
|
|
if (reg.openRootKey(rootKey) != RegError::NO_ERROR) {
|
|
throw css::uno::DeploymentException(
|
|
"Failure reading legacy rdb file " + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
RegistryKeyArray impls;
|
|
switch (rootKey.openSubKeys(u"IMPLEMENTATIONS"_ustr, impls)) {
|
|
case RegError::NO_ERROR:
|
|
break;
|
|
case RegError::KEY_NOT_EXISTS:
|
|
return true;
|
|
default:
|
|
throw css::uno::DeploymentException(
|
|
"Failure reading legacy rdb file " + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
for (sal_uInt32 i = 0; i != impls.getLength(); ++i) {
|
|
RegistryKey implKey(impls.getElement(i));
|
|
assert(implKey.getName().match("/IMPLEMENTATIONS/"));
|
|
OUString name(
|
|
implKey.getName().copy(RTL_CONSTASCII_LENGTH("/IMPLEMENTATIONS/")));
|
|
std::shared_ptr< Data::Implementation > impl =
|
|
std::make_shared<Data::Implementation>(
|
|
name, readLegacyRdbString(uri, implKey, u"UNO/ACTIVATOR"_ustr),
|
|
readLegacyRdbString(uri, implKey, u"UNO/LOCATION"_ustr), "", "", "", false,
|
|
css::uno::Reference< css::uno::XComponentContext >(), uri);
|
|
if (!data_.namedImplementations.emplace(name, impl).second)
|
|
{
|
|
throw css::registry::InvalidRegistryException(
|
|
uri + ": duplicate <implementation name=\"" + name + "\">");
|
|
}
|
|
readLegacyRdbStrings(
|
|
uri, implKey, u"UNO/SERVICES"_ustr, &impl->services);
|
|
for (const auto& rService : impl->services)
|
|
{
|
|
data_.services[rService].push_back(impl);
|
|
}
|
|
readLegacyRdbStrings(
|
|
uri, implKey, u"UNO/SINGLETONS"_ustr, &impl->singletons);
|
|
for (const auto& rSingleton : impl->singletons)
|
|
{
|
|
data_.singletons[rSingleton].push_back(impl);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
OUString cppuhelper::ServiceManager::readLegacyRdbString(
|
|
std::u16string_view uri, RegistryKey & key, OUString const & path)
|
|
{
|
|
RegistryKey subkey;
|
|
RegValueType t;
|
|
sal_uInt32 s(0);
|
|
if (key.openKey(path, subkey) != RegError::NO_ERROR
|
|
|| subkey.getValueInfo(OUString(), &t, &s) != RegError::NO_ERROR
|
|
|| t != RegValueType::STRING
|
|
|| s == 0 || s > o3tl::make_unsigned(SAL_MAX_INT32))
|
|
{
|
|
throw css::uno::DeploymentException(
|
|
OUString::Concat("Failure reading legacy rdb file ") + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
OUString val;
|
|
std::vector< char > v(s); // assuming sal_uInt32 fits into vector::size_type
|
|
assert(s > 0 && "throw above otherwise");
|
|
if (subkey.getValue(OUString(), v.data()) != RegError::NO_ERROR
|
|
|| v.back() != '\0'
|
|
|| !rtl_convertStringToUString(
|
|
&val.pData, v.data(), static_cast< sal_Int32 >(s - 1),
|
|
RTL_TEXTENCODING_UTF8,
|
|
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
|
|
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
|
|
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
|
|
{
|
|
throw css::uno::DeploymentException(
|
|
OUString::Concat("Failure reading legacy rdb file ") + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::readLegacyRdbStrings(
|
|
std::u16string_view uri, RegistryKey & key, OUString const & path,
|
|
std::vector< OUString > * strings)
|
|
{
|
|
assert(strings != nullptr);
|
|
RegistryKey subkey;
|
|
switch (key.openKey(path, subkey)) {
|
|
case RegError::NO_ERROR:
|
|
break;
|
|
case RegError::KEY_NOT_EXISTS:
|
|
return;
|
|
default:
|
|
throw css::uno::DeploymentException(
|
|
OUString::Concat("Failure reading legacy rdb file ") + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
OUString prefix(subkey.getName() + "/");
|
|
RegistryKeyNames names;
|
|
if (subkey.getKeyNames(OUString(), names) != RegError::NO_ERROR) {
|
|
throw css::uno::DeploymentException(
|
|
OUString::Concat("Failure reading legacy rdb file ") + uri,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
for (sal_uInt32 i = 0; i != names.getLength(); ++i) {
|
|
assert(names.getElement(i).match(prefix));
|
|
strings->push_back(names.getElement(i).copy(prefix.getLength()));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void cppuhelper::ServiceManager::insertRdbFiles(
|
|
std::vector< OUString > const & uris,
|
|
css::uno::Reference< css::uno::XComponentContext > const & alienContext)
|
|
{
|
|
Data extra;
|
|
for (const auto& rUri : uris)
|
|
{
|
|
try {
|
|
Parser(rUri, alienContext, &extra);
|
|
} catch (css::container::NoSuchElementException &) {
|
|
throw css::lang::IllegalArgumentException(
|
|
rUri + ": no such file", static_cast< cppu::OWeakObject * >(this),
|
|
0);
|
|
} catch (css::registry::InvalidRegistryException & e) {
|
|
throw css::lang::IllegalArgumentException(
|
|
"InvalidRegistryException: " + e.Message,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
}
|
|
insertExtraData(extra);
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::insertLegacyFactory(
|
|
css::uno::Reference< css::lang::XServiceInfo > const & factoryInfo)
|
|
{
|
|
assert(factoryInfo.is());
|
|
OUString name(factoryInfo->getImplementationName());
|
|
css::uno::Reference< css::lang::XSingleComponentFactory > f1(
|
|
factoryInfo, css::uno::UNO_QUERY);
|
|
css::uno::Reference< css::lang::XSingleServiceFactory > f2;
|
|
if (!f1.is()) {
|
|
f2.set(factoryInfo, css::uno::UNO_QUERY);
|
|
if (!f2.is()) {
|
|
throw css::lang::IllegalArgumentException(
|
|
(u"Bad XServiceInfo argument implements neither"
|
|
" XSingleComponentFactory nor XSingleServiceFactory"_ustr),
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
}
|
|
css::uno::Reference< css::lang::XComponent > comp(
|
|
factoryInfo, css::uno::UNO_QUERY);
|
|
std::shared_ptr< Data::Implementation > impl =
|
|
std::make_shared<Data::Implementation>(name, f1, f2, comp);
|
|
Data extra;
|
|
if (!name.isEmpty()) {
|
|
extra.namedImplementations.emplace(name, impl);
|
|
}
|
|
extra.dynamicImplementations.emplace(factoryInfo, impl);
|
|
const css::uno::Sequence< OUString > services(
|
|
factoryInfo->getSupportedServiceNames());
|
|
for (const auto & i : services) {
|
|
impl->services.push_back(i);
|
|
extra.services[i].push_back(impl);
|
|
}
|
|
if (insertExtraData(extra) && comp.is()) {
|
|
comp->addEventListener(this);
|
|
}
|
|
}
|
|
|
|
bool cppuhelper::ServiceManager::insertExtraData(Data const & extra) {
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
if (m_bDisposed) {
|
|
return false;
|
|
}
|
|
auto i = std::find_if(extra.namedImplementations.begin(), extra.namedImplementations.end(),
|
|
[this](const Data::NamedImplementations::value_type& rEntry) {
|
|
return data_.namedImplementations.find(rEntry.first) != data_.namedImplementations.end(); });
|
|
if (i != extra.namedImplementations.end())
|
|
{
|
|
throw css::lang::IllegalArgumentException(
|
|
"Insert duplicate implementation name " + i->first,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
bool bDuplicate = std::any_of(extra.dynamicImplementations.begin(), extra.dynamicImplementations.end(),
|
|
[this](const Data::DynamicImplementations::value_type& rEntry) {
|
|
return data_.dynamicImplementations.find(rEntry.first) != data_.dynamicImplementations.end(); });
|
|
if (bDuplicate)
|
|
{
|
|
throw css::lang::IllegalArgumentException(
|
|
u"Insert duplicate factory object"_ustr,
|
|
static_cast< cppu::OWeakObject * >(this), 0);
|
|
}
|
|
//TODO: The below leaves data_ in an inconsistent state upon exceptions:
|
|
data_.namedImplementations.insert(
|
|
extra.namedImplementations.begin(),
|
|
extra.namedImplementations.end());
|
|
data_.dynamicImplementations.insert(
|
|
extra.dynamicImplementations.begin(),
|
|
extra.dynamicImplementations.end());
|
|
insertImplementationMap(&data_.services, extra.services);
|
|
insertImplementationMap(&data_.singletons, extra.singletons);
|
|
}
|
|
//TODO: Updating the component context singleton data should be part of the
|
|
// atomic service manager update:
|
|
if (extra.singletons.empty())
|
|
return true;
|
|
|
|
assert(context_.is());
|
|
css::uno::Reference< css::container::XNameContainer > cont(
|
|
context_, css::uno::UNO_QUERY_THROW);
|
|
for (const auto& [rName, rImpls] : extra.singletons)
|
|
{
|
|
OUString name("/singletons/" + rName);
|
|
//TODO: Update should be atomic:
|
|
try {
|
|
cont->removeByName(name + "/arguments");
|
|
} catch (const css::container::NoSuchElementException &) {}
|
|
assert(!rImpls.empty());
|
|
assert(rImpls[0]);
|
|
SAL_INFO_IF(
|
|
rImpls.size() > 1, "cppuhelper",
|
|
"Arbitrarily choosing " << rImpls[0]->name
|
|
<< " among multiple implementations for singleton "
|
|
<< rName);
|
|
try {
|
|
cont->insertByName(
|
|
name + "/service", css::uno::Any(rImpls[0]->name));
|
|
} catch (css::container::ElementExistException &) {
|
|
cont->replaceByName(
|
|
name + "/service", css::uno::Any(rImpls[0]->name));
|
|
}
|
|
try {
|
|
cont->insertByName(name, css::uno::Any());
|
|
} catch (css::container::ElementExistException &) {
|
|
SAL_INFO("cppuhelper", "Overwriting singleton " << rName);
|
|
cont->replaceByName(name, css::uno::Any());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::removeRdbFiles(
|
|
std::vector< OUString > const & uris)
|
|
{
|
|
// The underlying data structures make this function somewhat inefficient,
|
|
// but the assumption is that it is rarely called (and that if it is called,
|
|
// it is called with a uris vector of size one):
|
|
std::vector< std::shared_ptr< Data::Implementation > > clear;
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
for (const auto& rUri : uris)
|
|
{
|
|
for (Data::NamedImplementations::iterator j(
|
|
data_.namedImplementations.begin());
|
|
j != data_.namedImplementations.end();)
|
|
{
|
|
assert(j->second);
|
|
if (j->second->rdbFile == rUri) {
|
|
clear.push_back(j->second);
|
|
//TODO: The below leaves data_ in an inconsistent state upon
|
|
// exceptions:
|
|
removeFromImplementationMap(
|
|
&data_.services, j->second->services, j->second);
|
|
removeFromImplementationMap(
|
|
&data_.singletons, j->second->singletons,
|
|
j->second);
|
|
j = data_.namedImplementations.erase(j);
|
|
} else {
|
|
++j;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//TODO: Update the component context singleton data
|
|
}
|
|
|
|
bool cppuhelper::ServiceManager::removeLegacyFactory(
|
|
css::uno::Reference< css::lang::XServiceInfo > const & factoryInfo,
|
|
bool removeListener)
|
|
{
|
|
assert(factoryInfo.is());
|
|
std::shared_ptr< Data::Implementation > clear;
|
|
css::uno::Reference< css::lang::XComponent > comp;
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
Data::DynamicImplementations::iterator i(
|
|
data_.dynamicImplementations.find(factoryInfo));
|
|
if (i == data_.dynamicImplementations.end()) {
|
|
return m_bDisposed;
|
|
}
|
|
assert(i->second);
|
|
clear = i->second;
|
|
if (removeListener) {
|
|
comp = i->second->component;
|
|
}
|
|
//TODO: The below leaves data_ in an inconsistent state upon exceptions:
|
|
removeFromImplementationMap(
|
|
&data_.services, i->second->services, i->second);
|
|
removeFromImplementationMap(
|
|
&data_.singletons, i->second->singletons, i->second);
|
|
if (!i->second->name.isEmpty()) {
|
|
data_.namedImplementations.erase(i->second->name);
|
|
}
|
|
data_.dynamicImplementations.erase(i);
|
|
}
|
|
if (comp.is()) {
|
|
removeEventListenerFromComponent(comp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cppuhelper::ServiceManager::removeImplementation(const OUString & name) {
|
|
// The underlying data structures make this function somewhat inefficient,
|
|
// but the assumption is that it is rarely called:
|
|
std::shared_ptr< Data::Implementation > clear;
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
if (m_bDisposed) {
|
|
return;
|
|
}
|
|
Data::NamedImplementations::iterator i(
|
|
data_.namedImplementations.find(name));
|
|
if (i == data_.namedImplementations.end()) {
|
|
throw css::container::NoSuchElementException(
|
|
"Remove non-inserted implementation " + name,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
assert(i->second);
|
|
clear = i->second;
|
|
//TODO: The below leaves data_ in an inconsistent state upon exceptions:
|
|
removeFromImplementationMap(
|
|
&data_.services, i->second->services, i->second);
|
|
removeFromImplementationMap(
|
|
&data_.singletons, i->second->singletons, i->second);
|
|
auto j = std::find_if(data_.dynamicImplementations.begin(), data_.dynamicImplementations.end(),
|
|
[&i](const Data::DynamicImplementations::value_type& rEntry) { return rEntry.second == i->second; });
|
|
if (j != data_.dynamicImplementations.end())
|
|
data_.dynamicImplementations.erase(j);
|
|
data_.namedImplementations.erase(i);
|
|
}
|
|
}
|
|
|
|
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
|
|
cppuhelper::ServiceManager::findServiceImplementation(
|
|
css::uno::Reference< css::uno::XComponentContext > const & context,
|
|
OUString const & specifier)
|
|
{
|
|
std::shared_ptr< Data::Implementation > impl;
|
|
bool loaded;
|
|
{
|
|
std::unique_lock g(m_aMutex);
|
|
Data::ImplementationMap::const_iterator i(
|
|
data_.services.find(specifier));
|
|
if (i == data_.services.end()) {
|
|
Data::NamedImplementations::const_iterator j(
|
|
data_.namedImplementations.find(specifier));
|
|
if (j == data_.namedImplementations.end()) {
|
|
SAL_INFO("cppuhelper", "No implementation for " << specifier);
|
|
return std::shared_ptr< Data::Implementation >();
|
|
}
|
|
impl = j->second;
|
|
} else {
|
|
assert(!i->second.empty());
|
|
SAL_INFO_IF(
|
|
i->second.size() > 1, "cppuhelper",
|
|
"Arbitrarily choosing " << i->second[0]->name
|
|
<< " among multiple implementations for " << i->first);
|
|
impl = i->second[0];
|
|
}
|
|
assert(impl);
|
|
loaded = impl->status == Data::Implementation::STATUS_LOADED;
|
|
}
|
|
if (!loaded) {
|
|
loadImplementation(context, impl);
|
|
}
|
|
return impl;
|
|
}
|
|
|
|
/// Make a simpler unique name for preload / progress reporting.
|
|
#ifndef DISABLE_DYNLOADING
|
|
static OUString simplifyModule(std::u16string_view uri)
|
|
{
|
|
sal_Int32 nIdx;
|
|
OUStringBuffer edit(uri);
|
|
if ((nIdx = edit.lastIndexOf('/')) > 0)
|
|
edit.remove(0,nIdx+1);
|
|
if ((nIdx = edit.lastIndexOf(':')) > 0)
|
|
edit.remove(0,nIdx+1);
|
|
if ((nIdx = edit.lastIndexOf("lo.so")) > 0)
|
|
edit.truncate(nIdx);
|
|
if ((nIdx = edit.lastIndexOf(".3")) > 0)
|
|
edit.truncate(nIdx);
|
|
if ((nIdx = edit.lastIndexOf("gcc3.so")) > 0)
|
|
edit.truncate(nIdx);
|
|
if ((nIdx = edit.lastIndexOf(".so")) > 0)
|
|
edit.truncate(nIdx);
|
|
if ((nIdx = edit.lastIndexOf("_uno")) > 0)
|
|
edit.truncate(nIdx);
|
|
if ((nIdx = edit.lastIndexOf(".jar")) > 0)
|
|
edit.truncate(nIdx);
|
|
if (edit.indexOf("lib") == 0)
|
|
edit.remove(0,3);
|
|
return edit.makeStringAndClear();
|
|
}
|
|
#endif
|
|
|
|
/// Used only by LibreOfficeKit when used by Online to pre-initialize
|
|
void cppuhelper::ServiceManager::preloadImplementations() {
|
|
#ifdef DISABLE_DYNLOADING
|
|
abort();
|
|
#else
|
|
OUString aUri;
|
|
std::unique_lock g(m_aMutex);
|
|
css::uno::Environment aSourceEnv(css::uno::Environment::getCurrent());
|
|
|
|
std::cerr << "preload:";
|
|
std::vector<OUString> aReported;
|
|
std::vector<OUString> aDisabled;
|
|
OUStringBuffer aDisabledMsg;
|
|
OUStringBuffer aMissingMsg;
|
|
|
|
/// Allow external callers & testers to disable certain components
|
|
const char *pDisable = getenv("UNODISABLELIBRARY");
|
|
if (pDisable)
|
|
{
|
|
OUString aDisable(pDisable, strlen(pDisable), RTL_TEXTENCODING_UTF8);
|
|
for (sal_Int32 i = 0; i >= 0; )
|
|
{
|
|
OUString tok( aDisable.getToken(0, ' ', i) );
|
|
tok = tok.trim();
|
|
if (!tok.isEmpty())
|
|
aDisabled.push_back(tok);
|
|
}
|
|
}
|
|
|
|
// loop all implementations
|
|
for (const auto& rEntry : data_.namedImplementations)
|
|
{
|
|
if (rEntry.second->loader != "com.sun.star.loader.SharedLibrary" ||
|
|
rEntry.second->status == Data::Implementation::STATUS_LOADED)
|
|
continue;
|
|
|
|
OUString simplified;
|
|
try
|
|
{
|
|
const OUString &aLibrary = rEntry.second->uri;
|
|
|
|
if (aLibrary.isEmpty())
|
|
continue;
|
|
|
|
simplified = simplifyModule(aLibrary);
|
|
|
|
bool bDisabled =
|
|
std::find(aDisabled.begin(), aDisabled.end(), simplified) != aDisabled.end();
|
|
|
|
if (std::find(aReported.begin(), aReported.end(), aLibrary) == aReported.end())
|
|
{
|
|
if (bDisabled)
|
|
{
|
|
aDisabledMsg.append(simplified + " ");
|
|
}
|
|
else
|
|
{
|
|
std::cerr << " " << simplified;
|
|
std::cerr.flush();
|
|
}
|
|
aReported.push_back(aLibrary);
|
|
}
|
|
|
|
if (bDisabled)
|
|
continue;
|
|
|
|
// expand absolute URI implementation component library
|
|
aUri = cppu::bootstrap_expandUri(aLibrary);
|
|
}
|
|
catch (css::lang::IllegalArgumentException& aError)
|
|
{
|
|
throw css::uno::DeploymentException(
|
|
"Cannot expand URI" + rEntry.second->uri + ": " + aError.Message,
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
|
|
// load component library
|
|
osl::Module aModule(aUri, SAL_LOADMODULE_NOW | SAL_LOADMODULE_GLOBAL);
|
|
|
|
if (!aModule.is())
|
|
{
|
|
aMissingMsg.append(simplified + " ");
|
|
}
|
|
|
|
if (aModule.is() &&
|
|
!rEntry.second->environment.isEmpty())
|
|
{
|
|
oslGenericFunction fpFactory;
|
|
css::uno::Environment aTargetEnv;
|
|
css::uno::Reference<css::uno::XInterface> xFactory;
|
|
|
|
if(rEntry.second->constructorName.isEmpty())
|
|
{
|
|
OUString aSymFactory;
|
|
// expand full name component factory symbol
|
|
if (rEntry.second->prefix == "direct")
|
|
aSymFactory = rEntry.second->name.replace('.', '_') + "_" COMPONENT_GETFACTORY;
|
|
else if (!rEntry.second->prefix.isEmpty())
|
|
aSymFactory = rEntry.second->prefix + "_" COMPONENT_GETFACTORY;
|
|
else
|
|
aSymFactory = COMPONENT_GETFACTORY;
|
|
|
|
// get function symbol component factory
|
|
fpFactory = aModule.getFunctionSymbol(aSymFactory);
|
|
if (fpFactory == nullptr)
|
|
{
|
|
throw css::loader::CannotActivateFactoryException(
|
|
("no factory symbol \"" + aSymFactory + "\" in component library :" + aUri),
|
|
css::uno::Reference<css::uno::XInterface>());
|
|
}
|
|
|
|
aTargetEnv = cppuhelper::detail::getEnvironment(rEntry.second->environment, rEntry.second->name);
|
|
component_getFactoryFunc fpComponentFactory = reinterpret_cast<component_getFactoryFunc>(fpFactory);
|
|
|
|
if (aSourceEnv.get() == aTargetEnv.get())
|
|
{
|
|
// invoke function component factory
|
|
OString aImpl(OUStringToOString(rEntry.second->name, RTL_TEXTENCODING_ASCII_US));
|
|
xFactory.set(css::uno::Reference<css::uno::XInterface>(static_cast<css::uno::XInterface *>(
|
|
(*fpComponentFactory)(aImpl.getStr(), this, nullptr)), SAL_NO_ACQUIRE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// get function symbol component factory
|
|
aTargetEnv = cppuhelper::detail::getEnvironment(rEntry.second->environment, rEntry.second->name);
|
|
fpFactory = (aSourceEnv.get() == aTargetEnv.get()) ?
|
|
aModule.getFunctionSymbol(rEntry.second->constructorName) : nullptr;
|
|
}
|
|
|
|
css::uno::Reference<css::lang::XSingleComponentFactory> xSCFactory;
|
|
css::uno::Reference<css::lang::XSingleServiceFactory> xSSFactory;
|
|
|
|
// query interface XSingleComponentFactory or XSingleServiceFactory
|
|
if (xFactory.is())
|
|
{
|
|
xSCFactory.set(xFactory, css::uno::UNO_QUERY);
|
|
if (!xSCFactory.is())
|
|
{
|
|
xSSFactory.set(xFactory, css::uno::UNO_QUERY);
|
|
if (!xSSFactory.is())
|
|
throw css::uno::DeploymentException(
|
|
("Implementation " + rEntry.second->name
|
|
+ " does not provide a constructor or factory"),
|
|
static_cast< cppu::OWeakObject * >(this));
|
|
}
|
|
}
|
|
|
|
if (!rEntry.second->constructorName.isEmpty() && fpFactory)
|
|
rEntry.second->constructorFn = WrapperConstructorFn(reinterpret_cast<ImplementationConstructorFn *>(fpFactory));
|
|
|
|
rEntry.second->factory1 = std::move(xSCFactory);
|
|
rEntry.second->factory2 = std::move(xSSFactory);
|
|
rEntry.second->status = Data::Implementation::STATUS_LOADED;
|
|
|
|
}
|
|
|
|
// Some libraries use other (non-UNO) libraries requiring preinit
|
|
oslGenericFunction fpPreload = aModule.getFunctionSymbol( "lok_preload_hook" );
|
|
if (fpPreload)
|
|
{
|
|
static std::vector<oslGenericFunction> aPreloaded;
|
|
if (std::find(aPreloaded.begin(), aPreloaded.end(), fpPreload) == aPreloaded.end())
|
|
{
|
|
aPreloaded.push_back(fpPreload);
|
|
// unlock because we may be instantiating some services here
|
|
g.unlock();
|
|
fpPreload();
|
|
g.lock();
|
|
}
|
|
}
|
|
|
|
// leak aModule
|
|
aModule.release();
|
|
}
|
|
std::cerr << std::endl;
|
|
|
|
if (aMissingMsg.getLength() > 0)
|
|
{
|
|
OUString aMsg = aMissingMsg.makeStringAndClear();
|
|
std::cerr << "Absent (often optional): " << aMsg << "\n";
|
|
}
|
|
if (aDisabledMsg.getLength() > 0)
|
|
{
|
|
OUString aMsg = aDisabledMsg.makeStringAndClear();
|
|
std::cerr << "Disabled: " << aMsg << "\n";
|
|
}
|
|
std::cerr.flush();
|
|
|
|
// Various rather important uno mappings.
|
|
static struct {
|
|
OUString maFrom;
|
|
OUString maTo;
|
|
OUString maPurpose;
|
|
} constexpr aMappingLoad[] = {
|
|
{ u"gcc3"_ustr, u"uno"_ustr, u""_ustr },
|
|
{ u"uno"_ustr, u"gcc3"_ustr, u""_ustr },
|
|
};
|
|
|
|
static std::vector<css::uno::Mapping> maMaps;
|
|
for (auto &it : aMappingLoad)
|
|
{
|
|
maMaps.push_back(css::uno::Mapping(it.maFrom, it.maTo, it.maPurpose));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|