/* -*- 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 <vector>
 
#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 <cppuhelper/bootstrap.hxx>
#include <cppuhelper/component_context.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/shlib.hxx>
#include <osl/file.hxx>
#include <osl/module.hxx>
#include <rtl/ref.hxx>
#include <rtl/uri.hxx>
#include <rtl/ustring.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <uno/environment.hxx>
#include <uno/mapping.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 (cppuhelper::ServiceManager::Data::ImplementationMap::const_iterator i(
             source.begin());
         i != source.end(); ++i)
    {
        std::vector<
            std::shared_ptr<
                cppuhelper::ServiceManager::Data::Implementation > > & impls
            = (*destination)[i->first];
        impls.insert(impls.end(), i->second.begin(), i->second.end());
    }
}
 
void removeFromImplementationMap(
    cppuhelper::ServiceManager::Data::ImplementationMap * map,
    std::vector< rtl::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 (std::vector< rtl::OUString >::const_iterator i(elements.begin());
         i != elements.end(); ++i)
    {
        cppuhelper::ServiceManager::Data::ImplementationMap::iterator j(
            map->find(*i));
        assert(j != map->end());
        std::vector<
            std::shared_ptr<
                cppuhelper::ServiceManager::Data::Implementation > >::iterator
            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(
        rtl::OUString const & uri,
        css::uno::Reference< css::uno::XComponentContext > const & alienContext,
        cppuhelper::ServiceManager::Data * data);
 
    Parser(const Parser&) = delete;
    const Parser& operator=(const Parser&) = delete;
 
private:
    void handleComponent();
 
    void handleImplementation();
 
    void handleService();
 
    void handleSingleton();
 
    rtl::OUString getNameAttribute();
 
    xmlreader::XmlReader reader_;
    css::uno::Reference< css::uno::XComponentContext > alienContext_;
    cppuhelper::ServiceManager::Data * data_;
    rtl::OUString attrLoader_;
    rtl::OUString attrUri_;
    rtl::OUString attrEnvironment_;
    rtl::OUString attrPrefix_;
    std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
        implementation_;
};
 
Parser::Parser(
    rtl::OUString const & uri,
    css::uno::Reference< css::uno::XComponentContext > const & alienContext,
    cppuhelper::ServiceManager::Data * data):
    reader_(uri), alienContext_(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;
            }
            SAL_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_ = rtl::OUString();
    attrUri_ = rtl::OUString();
    attrEnvironment_ = rtl::OUString();
    attrPrefix_ = rtl::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() {
    rtl::OUString attrName;
    rtl::OUString attrConstructor;
    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 {
            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_.reset(
        new cppuhelper::ServiceManager::Data::Implementation(
            attrName, attrLoader_, attrUri_, attrEnvironment_, attrConstructor,
            attrPrefix_, alienContext_, reader_.getUrl()));
    if (!data_->namedImplementations.emplace(attrName, implementation_).
        second)
    {
        throw css::registry::InvalidRegistryException(
            reader_.getUrl() + ": duplicate <implementation name=\"" + attrName
             + "\">");
    }
}
 
void Parser::handleService() {
    rtl::OUString name(getNameAttribute());
    implementation_->info->services.push_back(name);
    data_->services[name].push_back(implementation_);
}
 
void Parser::handleSingleton() {
    rtl::OUString name(getNameAttribute());
    implementation_->info->singletons.push_back(name);
    data_->singletons[name].push_back(implementation_);
}
 
rtl::OUString Parser::getNameAttribute() {
    rtl::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 > const & factories):
        factories_(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;
 
    osl::Mutex mutex_;
    std::vector< css::uno::Any > factories_;
    std::vector< css::uno::Any >::const_iterator iterator_;
};
 
sal_Bool ContentEnumeration::hasMoreElements()
{
    osl::MutexGuard g(mutex_);
    return iterator_ != factories_.end();
}
 
css::uno::Any ContentEnumeration::nextElement()
{
    osl::MutexGuard g(mutex_);
    if (iterator_ == factories_.end()) {
        throw css::container::NoSuchElementException(
            "Bootstrap service manager service enumerator has no more elements",
            static_cast< cppu::OWeakObject * >(this));
    }
    return *iterator_++;
}
 
css::beans::Property getDefaultContextProperty() {
    return css::beans::Property(
        "DefaultContext", -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.get() != nullptr); }
 
    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.get() != nullptr); }
 
    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 rtl::OUString SAL_CALL getImplementationName() override;
 
    virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) override;
 
    virtual css::uno::Sequence< rtl::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());
}
 
rtl::OUString ImplementationWrapper::getImplementationName()
{
    std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
    assert(impl);
    return impl->info->name;
}
 
sal_Bool ImplementationWrapper::supportsService(rtl::OUString const & ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}
 
css::uno::Sequence< rtl::OUString >
ImplementationWrapper::getSupportedServiceNames()
{
    std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation > impl = implementation_.lock();
    assert(impl);
    if (impl->info->services.size()
        > static_cast< sal_uInt32 >(SAL_MAX_INT32))
    {
        throw css::uno::RuntimeException(
            ("Implementation " + impl->info->name
             + " supports too many services"),
            static_cast< cppu::OWeakObject * >(this));
    }
    css::uno::Sequence< rtl::OUString > names(
        static_cast< sal_Int32 >(impl->info->services.size()));
    sal_Int32 i = 0;
    for (std::vector< rtl::OUString >::const_iterator j(
             impl->info->services.begin());
         j != impl->info->services.end(); ++j)
    {
        names[i++] = *j;
    }
    return names;
}
 
}
 
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 (constructor) {
        inst.set(
            constructor(context.get(), css::uno::Sequence<css::uno::Any>()),
            SAL_NO_ACQUIRE);
    } else if (factory1.is()) {
            inst = factory1->createInstanceWithContext(context);
    } else {
        assert(factory2.is());
        inst = factory2->createInstance();
    }
    updateDisposeSingleton(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 (constructor) {
        inst.set(constructor(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);
        }
    } else if (factory1.is()) {
        inst = factory1->createInstanceWithArgumentsAndContext(
            arguments, context);
    } else {
        assert(factory2.is());
        inst = factory2->createInstanceWithArguments(arguments);
    }
    updateDisposeSingleton(singletonRequest, inst);
    return inst;
}
 
void cppuhelper::ServiceManager::Data::Implementation::updateDisposeSingleton(
    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) {
        osl::MutexGuard g(mutex);
        disposeSingleton.clear();
        dispose = false;
    } else if (!info->singletons.empty()) {
        css::uno::Reference<css::lang::XComponent> comp(
            instance, css::uno::UNO_QUERY);
        if (comp.is()) {
            osl::MutexGuard g(mutex);
            if (dispose) {
                disposeSingleton = comp;
            }
        }
    }
}
 
void cppuhelper::ServiceManager::addSingletonContextEntries(
    std::vector< cppu::ContextEntry_Init > * entries)
{
    assert(entries != nullptr);
    for (Data::ImplementationMap::const_iterator i(data_.singletons.begin());
         i != data_.singletons.end(); ++i)
    {
        assert(!i->second.empty());
        assert(i->second[0].get() != nullptr);
        SAL_INFO_IF(
            i->second.size() > 1, "cppuhelper",
            "Arbitrarily choosing " << i->second[0]->info->name
                << " among multiple implementations for " << i->first);
        entries->push_back(
            cppu::ContextEntry_Init(
                "/singletons/" + i->first,
                css::uno::makeAny<
                    css::uno::Reference<css::lang::XSingleComponentFactory> >(
                        new SingletonFactory(this, i->second[0])),
                true));
    }
}
 
void cppuhelper::ServiceManager::loadImplementation(
        css::uno::Reference< css::uno::XComponentContext > const & context,
        std::shared_ptr< Data::Implementation > const & implementation)
{
    assert(implementation.get() != nullptr);
    {
        osl::MutexGuard g(rBHelper.rMutex);
        if (implementation->status == Data::Implementation::STATUS_LOADED) {
            return;
        }
    }
    rtl::OUString uri;
    try {
        uri = cppu::bootstrap_expandUri(implementation->info->uri);
    } catch (css::lang::IllegalArgumentException & e) {
        throw css::uno::DeploymentException(
            "Cannot expand URI" + implementation->info->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->info->alienContext.is()
        && implementation->info->loader == "com.sun.star.loader.SharedLibrary")
    {
        cppuhelper::detail::loadSharedLibComponentFactory(
            uri, implementation->info->environment,
            implementation->info->prefix, implementation->info->name,
            implementation->info->constructor, this, &ctor, &f0);
        if (ctor) {
            assert(!implementation->info->environment.isEmpty());
        }
    } else {
        SAL_WARN_IF(
            !implementation->info->environment.isEmpty(), "cppuhelper",
            "Loader " << implementation->info->loader
                << " and non-empty environment "
                << implementation->info->environment);
        SAL_WARN_IF(
            !implementation->info->prefix.isEmpty(), "cppuhelper",
            "Loader " << implementation->info->loader
                << " and non-empty constructor "
                << implementation->info->constructor);
        SAL_WARN_IF(
            !implementation->info->prefix.isEmpty(), "cppuhelper",
            "Loader " << implementation->info->loader
                << " and non-empty prefix " << implementation->info->prefix);
        css::uno::Reference< css::uno::XComponentContext > ctxt;
        css::uno::Reference< css::lang::XMultiComponentFactory > smgr;
        if (implementation->info->alienContext.is()) {
            ctxt = implementation->info->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->info->loader, ctxt),
            css::uno::UNO_QUERY_THROW);
        f0 = loader->activate(
            implementation->info->name, rtl::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->info->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.
    osl::MutexGuard g(rBHelper.rMutex);
    if (!(isDisposed()
          || implementation->status == Data::Implementation::STATUS_LOADED))
    {
        implementation->status = Data::Implementation::STATUS_LOADED;
        implementation->constructor = ctor;
        implementation->factory1 = f1;
        implementation->factory2 = f2;
    }
}
 
void cppuhelper::ServiceManager::disposing() {
    std::vector< css::uno::Reference<css::lang::XComponent> > sngls;
    std::vector< css::uno::Reference< css::lang::XComponent > > comps;
    Data clear;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        for (Data::NamedImplementations::const_iterator i(
                 data_.namedImplementations.begin());
             i != data_.namedImplementations.end(); ++i)
        {
            assert(i->second.get() != nullptr);
            if (!i->second->info->singletons.empty()) {
                osl::MutexGuard g2(i->second->mutex);
                if (i->second->disposeSingleton.is()) {
                    sngls.push_back(i->second->disposeSingleton);
                }
            }
        }
        for (Data::DynamicImplementations::const_iterator i(
                 data_.dynamicImplementations.begin());
             i != data_.dynamicImplementations.end(); ++i)
        {
            assert(i->second.get() != nullptr);
            if (!i->second->info->singletons.empty()) {
                osl::MutexGuard g2(i->second->mutex);
                if (i->second->disposeSingleton.is()) {
                    sngls.push_back(i->second->disposeSingleton);
                }
            }
            if (i->second->component.is()) {
                comps.push_back(i->second->component);
            }
        }
        data_.namedImplementations.swap(clear.namedImplementations);
        data_.dynamicImplementations.swap(clear.dynamicImplementations);
        data_.services.swap(clear.services);
        data_.singletons.swap(clear.singletons);
    }
    for (std::vector<
             css::uno::Reference<css::lang::XComponent> >::const_iterator i(
                 sngls.begin());
         i != sngls.end(); ++i)
    {
        try {
            (*i)->dispose();
        } catch (css::uno::RuntimeException & e) {
            SAL_WARN("cppuhelper", "Ignoring " << e << " while disposing singleton");
        }
    }
    for (std::vector<
             css::uno::Reference< css::lang::XComponent > >::const_iterator i(
                 comps.begin());
         i != comps.end(); ++i)
    {
        removeEventListenerFromComponent(*i);
    }
}
 
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(
            "invalid ServiceManager::initialize argument",
            css::uno::Reference<css::uno::XInterface>(), 0);
    }
    preloadImplementations();
}
 
rtl::OUString cppuhelper::ServiceManager::getImplementationName()
{
    return rtl::OUString(
        "com.sun.star.comp.cppuhelper.bootstrap.ServiceManager");
}
 
sal_Bool cppuhelper::ServiceManager::supportsService(
    rtl::OUString const & ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}
 
css::uno::Sequence< rtl::OUString >
cppuhelper::ServiceManager::getSupportedServiceNames()
{
    css::uno::Sequence< rtl::OUString > names(2);
    names[0] = "com.sun.star.lang.MultiServiceFactory";
    names[1] = "com.sun.star.lang.ServiceManager";
    return names;
}
 
css::uno::Reference< css::uno::XInterface >
cppuhelper::ServiceManager::createInstance(
    rtl::OUString const & aServiceSpecifier)
{
    assert(context_.is());
    return createInstanceWithContext(aServiceSpecifier, context_);
}
 
css::uno::Reference< css::uno::XInterface >
cppuhelper::ServiceManager::createInstanceWithArguments(
    rtl::OUString const & ServiceSpecifier,
    css::uno::Sequence< css::uno::Any > const & Arguments)
{
    assert(context_.is());
    return createInstanceWithArgumentsAndContext(
        ServiceSpecifier, Arguments, context_);
}
 
css::uno::Sequence< rtl::OUString >
cppuhelper::ServiceManager::getAvailableServiceNames()
{
    osl::MutexGuard g(rBHelper.rMutex);
    if (isDisposed()) {
        return css::uno::Sequence< rtl::OUString >();
    }
    Data::ImplementationMap::size_type n = data_.services.size();
    if (n > static_cast< sal_uInt32 >(SAL_MAX_INT32)) {
        throw css::uno::RuntimeException(
            "getAvailableServiceNames: too many services",
            static_cast< cppu::OWeakObject * >(this));
    }
    css::uno::Sequence< rtl::OUString > names(static_cast< sal_Int32 >(n));
    sal_Int32 i = 0;
    for (Data::ImplementationMap::const_iterator j(data_.services.begin());
         j != data_.services.end(); ++j)
    {
        names[i++] = j->first;
    }
    assert(i == names.getLength());
    return names;
}
 
css::uno::Reference< css::uno::XInterface >
cppuhelper::ServiceManager::createInstanceWithContext(
    rtl::OUString const & aServiceSpecifier,
    css::uno::Reference< css::uno::XComponentContext > const & Context)
{
    std::shared_ptr< Data::Implementation > impl(
        findServiceImplementation(Context, aServiceSpecifier));
    return impl.get() == nullptr
        ? css::uno::Reference< css::uno::XInterface >()
        : impl->createInstance(Context, false);
}
 
css::uno::Reference< css::uno::XInterface >
cppuhelper::ServiceManager::createInstanceWithArgumentsAndContext(
    rtl::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.get() == 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()
{
    osl::MutexGuard g(rBHelper.rMutex);
    return
        !(data_.namedImplementations.empty()
          && data_.dynamicImplementations.empty());
}
 
css::uno::Reference< css::container::XEnumeration >
cppuhelper::ServiceManager::createEnumeration()
{
    throw css::uno::RuntimeException(
        "ServiceManager createEnumeration: method not supported",
        static_cast< cppu::OWeakObject * >(this));
}
 
sal_Bool cppuhelper::ServiceManager::has(css::uno::Any const &)
{
    throw css::uno::RuntimeException(
        "ServiceManager has: method not supported",
        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< rtl::OUString > uris;
        css::uno::Reference< css::uno::XComponentContext > alienContext;
        for (sal_Int32 i = 0; i < args.getLength(); ++i) {
            if (args[i].Name == "uri") {
                rtl::OUString uri;
                if (!(args[i].Value >>= uri)) {
                    throw css::lang::IllegalArgumentException(
                        "Bad uri argument",
                        static_cast< cppu::OWeakObject * >(this), 0);
                }
                uris.push_back(uri);
            } else if (args[i].Name == "component-context") {
                if (alienContext.is()) {
                    throw css::lang::IllegalArgumentException(
                        "Multiple component-context arguments",
                        static_cast< cppu::OWeakObject * >(this), 0);
                }
                if (!(args[i].Value >>= alienContext) || !alienContext.is()) {
                    throw css::lang::IllegalArgumentException(
                        "Bad component-context argument",
                        static_cast< cppu::OWeakObject * >(this), 0);
                }
            } else {
                throw css::lang::IllegalArgumentException(
                    "Bad argument " + args[i].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;
    }
// At least revisions up to 1.7 of LanguageTool.oxt (incl. the bundled 1.4.0 in
// module languagetool) contain an (actively registered) factory that does not
// implement XServiceInfo (see <http://sourceforge.net/tracker/?
// func=detail&aid=3526635&group_id=110216&atid=655717> "SingletonFactory should
// implement XServiceInfo"); the old OServiceManager::insert
// (stoc/source/servicemanager/servicemanager.cxx) silently did not add such
// broken factories to its m_ImplementationNameMap, so ignore them here for
// backwards compatibility of live-insertion of extensions, too.
 
// (The plan was that this warning would go away (and we would do the
// throw instead) for the incompatible LO 4, but we changed our mind):
    css::uno::Reference< css::lang::XSingleComponentFactory > legacy;
    if ((aElement >>= legacy) && legacy.is()) {
        SAL_WARN(
            "cppuhelper",
            "Ignored XSingleComponentFactory not implementing XServiceInfo");
        return;
    }
 
    throw css::lang::IllegalArgumentException(
        "Bad insert element", 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< rtl::OUString > uris;
        for (sal_Int32 i = 0; i < args.getLength(); ++i) {
            if (args[i].Name != "uri") {
                throw css::lang::IllegalArgumentException(
                    "Bad argument " + args[i].Name,
                    static_cast< cppu::OWeakObject * >(this), 0);
            }              rtl::OUString uri;
            if (!(args[i].Value >>= uri)) {
                throw css::lang::IllegalArgumentException(
                    "Bad uri argument",
                    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(
                "Remove non-inserted factory object",
                static_cast< cppu::OWeakObject * >(this));
        }
        return;
    }
    rtl::OUString impl;
    if (aElement >>= impl) {
        // For live-removal of extensions:
        removeImplementation(impl);
        return;
    }
    throw css::lang::IllegalArgumentException(
        "Bad remove element", static_cast< cppu::OWeakObject * >(this), 0);
}
 
css::uno::Reference< css::container::XEnumeration >
cppuhelper::ServiceManager::createContentEnumeration(
    rtl::OUString const & aServiceName)
{
    std::vector< std::shared_ptr< Data::Implementation > > impls;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        Data::ImplementationMap::const_iterator i(
            data_.services.find(aServiceName));
        if (i != data_.services.end()) {
            impls = i->second;
        }
    }
    std::vector< css::uno::Any > factories;
    for (std::vector<
             std::shared_ptr< Data::Implementation > >::const_iterator i(
                 impls.begin());
         i != impls.end(); ++i)
    {
        Data::Implementation * impl = i->get();
        assert(impl != nullptr);
        {
            osl::MutexGuard g(rBHelper.rMutex);
            if (isDisposed()) {
                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, *i);
                impl->status = Data::Implementation::STATUS_WRAPPER;
            }
            if (impl->constructor != nullptr && !impl->factory1.is()) {
                impl->factory1 = new ImplementationWrapper(this, *i);
            }
        }
        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(factories);
}
 
css::uno::Reference< css::beans::XPropertySetInfo >
cppuhelper::ServiceManager::getPropertySetInfo()
{
    return this;
}
 
void cppuhelper::ServiceManager::setPropertyValue(
    rtl::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(
    rtl::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(
    rtl::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(
    rtl::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(
    rtl::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(
    rtl::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() {
    css::uno::Sequence< css::beans::Property > props(1);
    props[0] = getDefaultContextProperty();
    return props;
}
 
css::beans::Property cppuhelper::ServiceManager::getPropertyByName(
    rtl::OUString const & aName)
{
    if (aName != "DefaultContext") {
        throw css::beans::UnknownPropertyException(
            aName, static_cast< cppu::OWeakObject * >(this));
    }
    return getDefaultContextProperty();
}
 
sal_Bool cppuhelper::ServiceManager::hasPropertyByName(
    rtl::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(rtl::OUString const & rdbUris) {
    for (sal_Int32 i = 0; i != -1;) {
        rtl::OUString uri(rdbUris.getToken(0, ' ', i));
        if (uri.isEmpty()) {
            continue;
        }
        bool optional;
        bool directory;
        cppu::decodeRdbUri(&uri, &optional, &directory);
        if (directory) {
            readRdbDirectory(uri, optional);
        } else {
            readRdbFile(uri, optional);
        }
    }
}
 
void cppuhelper::ServiceManager::readRdbDirectory(
    rtl::OUString const & uri, bool optional)
{
    osl::Directory dir(uri);
    switch (dir.open()) {
    case osl::FileBase::E_None:
        break;
    case osl::FileBase::E_NOENT:
        if (optional) {
            SAL_INFO("cppuhelper", "Ignored optional " << uri);
            return;
        }
        SAL_FALLTHROUGH;
    default:
        throw css::uno::DeploymentException(
            "Cannot open directory " + uri,
            static_cast< cppu::OWeakObject * >(this));
    }
    for (;;) {
        rtl::OUString url;
        if (!cppu::nextDirectoryItem(dir, &url)) {
            break;
        }
        readRdbFile(url, false);
    }
}
 
void cppuhelper::ServiceManager::readRdbFile(
    rtl::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);
    } 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;
        }
    }
}
 
bool cppuhelper::ServiceManager::readLegacyRdbFile(rtl::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;
                }
            }
        }
        SAL_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("IMPLEMENTATIONS", 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/"));
        rtl::OUString name(
            implKey.getName().copy(RTL_CONSTASCII_LENGTH("/IMPLEMENTATIONS/")));
        std::shared_ptr< Data::Implementation > impl(
            new Data::Implementation(
                name, readLegacyRdbString(uri, implKey, "UNO/ACTIVATOR"),
                readLegacyRdbString(uri, implKey, "UNO/LOCATION"), "", "", "",
                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, "UNO/SERVICES", &impl->info->services);
        for (std::vector< rtl::OUString >::const_iterator j(
                 impl->info->services.begin());
             j != impl->info->services.end(); ++j)
        {
            data_.services[*j].push_back(impl);
        }
        readLegacyRdbStrings(
            uri, implKey, "UNO/SINGLETONS", &impl->info->singletons);
        for (std::vector< rtl::OUString >::const_iterator j(
                 impl->info->singletons.begin());
             j != impl->info->singletons.end(); ++j)
        {
            data_.singletons[*j].push_back(impl);
        }
    }
    return true;
}
 
rtl::OUString cppuhelper::ServiceManager::readLegacyRdbString(
    rtl::OUString const & uri, RegistryKey & key, rtl::OUString const & path)
{
    RegistryKey subkey;
    RegValueType t;
    sal_uInt32 s(0);
    if (key.openKey(path, subkey) != RegError::NO_ERROR
        || subkey.getValueInfo(rtl::OUString(), &t, &s) != RegError::NO_ERROR
        || t != RegValueType::STRING
        || s == 0 || s > static_cast< sal_uInt32 >(SAL_MAX_INT32))
    {
        throw css::uno::DeploymentException(
            "Failure reading legacy rdb file " + uri,
            static_cast< cppu::OWeakObject * >(this));
    }
    rtl::OUString val;
    std::vector< char > v(s); // assuming sal_uInt32 fits into vector::size_type
    if (subkey.getValue(rtl::OUString(), &v[0]) != RegError::NO_ERROR
        || v.back() != '\0'
        || !rtl_convertStringToUString(
            &val.pData, &v[0], 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(
            "Failure reading legacy rdb file " + uri,
            static_cast< cppu::OWeakObject * >(this));
    }
    return val;
}
 
void cppuhelper::ServiceManager::readLegacyRdbStrings(
    rtl::OUString const & uri, RegistryKey & key, rtl::OUString const & path,
    std::vector< rtl::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(
            "Failure reading legacy rdb file " + uri,
            static_cast< cppu::OWeakObject * >(this));
    }
    rtl::OUString prefix(subkey.getName() + "/");
    RegistryKeyNames names;
    if (subkey.getKeyNames(rtl::OUString(), names) != RegError::NO_ERROR) {
        throw css::uno::DeploymentException(
            "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()));
    }
}
 
void cppuhelper::ServiceManager::insertRdbFiles(
    std::vector< rtl::OUString > const & uris,
    css::uno::Reference< css::uno::XComponentContext > const & alienContext)
{
    Data extra;
    for (std::vector< rtl::OUString >::const_iterator i(uris.begin());
         i != uris.end(); ++i)
    {
        try {
            Parser(*i, alienContext, &extra);
        } catch (css::container::NoSuchElementException &) {
            throw css::lang::IllegalArgumentException(
                *i + ": 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());
    rtl::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(
                ("Bad XServiceInfo argument implements neither"
                 " XSingleComponentFactory nor XSingleServiceFactory"),
                static_cast< cppu::OWeakObject * >(this), 0);
        }
    }
    css::uno::Reference< css::lang::XComponent > comp(
        factoryInfo, css::uno::UNO_QUERY);
    std::shared_ptr< Data::Implementation > impl(
        new Data::Implementation(name, f1, f2, comp));
    Data extra;
    if (!name.isEmpty()) {
        extra.namedImplementations.emplace(name, impl);
    }
    extra.dynamicImplementations.emplace(factoryInfo, impl);
    css::uno::Sequence< rtl::OUString > services(
        factoryInfo->getSupportedServiceNames());
    for (sal_Int32 i = 0; i != services.getLength(); ++i) {
        impl->info->services.push_back(services[i]);
        extra.services[services[i]].push_back(impl);
    }
    if (insertExtraData(extra) && comp.is()) {
        comp->addEventListener(this);
    }
}
 
bool cppuhelper::ServiceManager::insertExtraData(Data const & extra) {
    {
        osl::MutexGuard g(rBHelper.rMutex);
        if (isDisposed()) {
            return false;
        }
        for (Data::NamedImplementations::const_iterator i(
                 extra.namedImplementations.begin());
             i != extra.namedImplementations.end(); ++i)
        {
            if (data_.namedImplementations.find(i->first)
                != data_.namedImplementations.end())
            {
                throw css::lang::IllegalArgumentException(
                    "Insert duplicate implementation name " + i->first,
                    static_cast< cppu::OWeakObject * >(this), 0);
            }
        }
        for (Data::DynamicImplementations::const_iterator i(
                 extra.dynamicImplementations.begin());
             i != extra.dynamicImplementations.end(); ++i)
        {
            if (data_.dynamicImplementations.find(i->first)
                != data_.dynamicImplementations.end())
            {
                throw css::lang::IllegalArgumentException(
                    "Insert duplicate factory object",
                    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()) {
        assert(context_.is());
        css::uno::Reference< css::container::XNameContainer > cont(
            context_, css::uno::UNO_QUERY_THROW);
        for (Data::ImplementationMap::const_iterator i(
                 extra.singletons.begin());
             i != extra.singletons.end(); ++i)
        {
            rtl::OUString name("/singletons/" + i->first);
            //TODO: Update should be atomic:
            try {
                cont->removeByName(name + "/arguments");
            } catch (const css::container::NoSuchElementException &) {}
            assert(!i->second.empty());
            assert(i->second[0].get() != nullptr);
            SAL_INFO_IF(
                i->second.size() > 1, "cppuhelper",
                "Arbitrarily choosing " << i->second[0]->info->name
                    << " among multiple implementations for singleton "
                    << i->first);
            try {
                cont->insertByName(
                    name + "/service", css::uno::Any(i->second[0]->info->name));
            } catch (css::container::ElementExistException &) {
                cont->replaceByName(
                    name + "/service", css::uno::Any(i->second[0]->info->name));
            }
            try {
                cont->insertByName(name, css::uno::Any());
            } catch (css::container::ElementExistException &) {
                SAL_INFO("cppuhelper", "Overwriting singleton " << i->first);
                cont->replaceByName(name, css::uno::Any());
            }
        }
    }
    return true;
}
 
void cppuhelper::ServiceManager::removeRdbFiles(
    std::vector< rtl::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;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        for (std::vector< rtl::OUString >::const_iterator i(uris.begin());
             i != uris.end(); ++i)
        {
            for (Data::NamedImplementations::iterator j(
                     data_.namedImplementations.begin());
                 j != data_.namedImplementations.end();)
            {
                assert(j->second.get() != nullptr);
                if (j->second->info->rdbFile == *i) {
                    clear.push_back(j->second);
                    //TODO: The below leaves data_ in an inconsistent state upon
                    // exceptions:
                    removeFromImplementationMap(
                        &data_.services, j->second->info->services, j->second);
                    removeFromImplementationMap(
                        &data_.singletons, j->second->info->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;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        Data::DynamicImplementations::iterator i(
            data_.dynamicImplementations.find(factoryInfo));
        if (i == data_.dynamicImplementations.end()) {
            return isDisposed();
        }
        assert(i->second.get() != nullptr);
        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->info->services, i->second);
        removeFromImplementationMap(
            &data_.singletons, i->second->info->singletons, i->second);
        if (!i->second->info->name.isEmpty()) {
            data_.namedImplementations.erase(i->second->info->name);
        }
        data_.dynamicImplementations.erase(i);
    }
    if (comp.is()) {
        removeEventListenerFromComponent(comp);
    }
    return true;
}
 
void cppuhelper::ServiceManager::removeImplementation(const rtl::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;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        if (isDisposed()) {
            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.get() != nullptr);
        clear = i->second;
        //TODO: The below leaves data_ in an inconsistent state upon exceptions:
        removeFromImplementationMap(
            &data_.services, i->second->info->services, i->second);
        removeFromImplementationMap(
            &data_.singletons, i->second->info->singletons, i->second);
        for (Data::DynamicImplementations::iterator j(
                 data_.dynamicImplementations.begin());
             j != data_.dynamicImplementations.end(); ++j)
        {
            if (j->second == i->second) {
                data_.dynamicImplementations.erase(j);
                break;
            }
        }
        data_.namedImplementations.erase(i);
    }
}
 
std::shared_ptr< cppuhelper::ServiceManager::Data::Implementation >
cppuhelper::ServiceManager::findServiceImplementation(
    css::uno::Reference< css::uno::XComponentContext > const & context,
    rtl::OUString const & specifier)
{
    std::shared_ptr< Data::Implementation > impl;
    bool loaded;
    {
        osl::MutexGuard g(rBHelper.rMutex);
        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]->info->name
                    << " among multiple implementations for " << i->first);
            impl = i->second[0];
        }
        assert(impl.get() != nullptr);
        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 rtl::OUString simplifyModule(const rtl::OUString &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
    rtl::OUString aUri;
    osl::MutexGuard g(rBHelper.rMutex);
    css::uno::Environment aSourceEnv(css::uno::Environment::getCurrent());
 
    std::cerr << "preload:";
    std::vector<OUString> aReported;
 
    // loop all implementations
    for (Data::NamedImplementations::const_iterator iterator(
            data_.namedImplementations.begin());
            iterator != data_.namedImplementations.end(); ++iterator)
    {
        try
        {
            const rtl::OUString &aLibrary = iterator->second->info->uri;
 
            if (aLibrary.isEmpty())
                continue;
 
            if (std::find(aReported.begin(), aReported.end(), aLibrary) == aReported.end())
            {
                std::cerr << " " << simplifyModule(aLibrary);
                std::cerr.flush();
                aReported.push_back(aLibrary);
            }
 
            // expand absolute URI implementation component library
            aUri = cppu::bootstrap_expandUri(aLibrary);
        }
        catch (css::lang::IllegalArgumentException& aError)
        {
            throw css::uno::DeploymentException(
                "Cannot expand URI" + iterator->second->info->uri + ": " + aError.Message,
                static_cast< cppu::OWeakObject * >(this));
        }
 
        if (iterator->second->info->loader == "com.sun.star.loader.SharedLibrary" &&
            iterator->second->status != Data::Implementation::STATUS_LOADED)
        {
            // Blacklist some components that are known to fail
            if (iterator->second->info->name == "com.sun.star.comp.configuration.backend.KDE4Backend")
            {
                std::cerr << ":skipping";
                std::cerr.flush();
                continue;
            }
 
            // load component library
            osl::Module aModule(aUri, SAL_LOADMODULE_NOW | SAL_LOADMODULE_GLOBAL);
 
            if (!aModule.is())
            {
                std::cerr << ":failed" << std::endl;
                std::cerr.flush();
            }
 
            if (aModule.is() &&
                !iterator->second->info->environment.isEmpty())
            {
                OUString aSymFactory;
                oslGenericFunction fpFactory;
                css::uno::Environment aTargetEnv;
                css::uno::Reference<css::uno::XInterface> xFactory;
 
                if(iterator->second->info->constructor.isEmpty())
                {
                    // expand full name component factory symbol
                    if (iterator->second->info->prefix == "direct")
                        aSymFactory = iterator->second->info->name.replace('.', '_') + "_" COMPONENT_GETFACTORY;
                    else if (!iterator->second->info->prefix.isEmpty())
                        aSymFactory = iterator->second->info->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(iterator->second->info->environment, iterator->second->info->name);
                    component_getFactoryFunc fpComponentFactory = reinterpret_cast<component_getFactoryFunc>(fpFactory);
 
                    if (aSourceEnv.get() == aTargetEnv.get())
                    {
                        // invoke function component factory
                        OString aImpl(rtl::OUStringToOString(iterator->second->info->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(iterator->second->info->environment, iterator->second->info->name);
                    if (aSourceEnv.get() == aTargetEnv.get())
                    {
                        fpFactory = aModule.getFunctionSymbol(iterator->second->info->constructor);
                    }
                    else
                    {
                        fpFactory = 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 " + iterator->second->info->name
                                  + " does not provide a constructor or factory"),
                                static_cast< cppu::OWeakObject * >(this));
                        }
                    }
                }
 
                if (!iterator->second->info->constructor.isEmpty() && fpFactory)
                    iterator->second->constructor = WrapperConstructorFn(reinterpret_cast<ImplementationConstructorFn *>(fpFactory));
 
                iterator->second->factory1 = xSCFactory;
                iterator->second->factory2 = xSSFactory;
                iterator->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);
                    fpPreload();
                }
            }
 
            // leak aModule
            aModule.release();
        }
    }
    std::cerr << std::endl;
 
    // Various rather important uno mappings.
    struct {
        const char *mpFrom;
        const char *mpTo;
        const char *mpPurpose;
    } const aMappingLoad[] = {
        { "gcc3", "uno",  "" },
        { "uno",  "gcc3", "" },
    };
 
    static std::vector<css::uno::Mapping> maMaps;
    for (auto &it : aMappingLoad)
    {
        maMaps.push_back(css::uno::Mapping(
                             OUString::createFromAscii(it.mpFrom),
                             OUString::createFromAscii(it.mpTo),
                             OUString::createFromAscii(it.mpPurpose)));
    }
#endif
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1019 Compound assignment expression 'aArguments[0] >>= arg' is used inside condition.