/* -*- 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <sal/config.h>
 
#include <o3tl/any.hxx>
#include <osl/mutex.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
 
#include <cppuhelper/basemutex.hxx>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/weakref.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <comphelper/sequence.hxx>
#include <rtl/ref.hxx>
 
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XEventListener.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <com/sun/star/registry/XSimpleRegistry.hpp>
#include <com/sun/star/container/XSet.hpp>
#include <com/sun/star/container/XElementAccess.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
 
#include <iterator>
#include <mutex>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <utility>
 
using namespace com::sun::star;
using namespace css::uno;
using namespace css::beans;
using namespace css::registry;
using namespace css::lang;
using namespace css::container;
using namespace cppu;
using namespace osl;
 
namespace {
 
Sequence< OUString > retrieveAsciiValueList(
    const Reference< XSimpleRegistry > &xReg, const OUString &keyName )
{
    Reference< XEnumerationAccess > xAccess( xReg, UNO_QUERY );
    Sequence< OUString > seq;
    if( xAccess.is() )
    {
        Reference< XEnumeration > xEnum = xAccess->createEnumeration();
        while( xEnum.is() && xEnum->hasMoreElements() )
        {
            Reference< XSimpleRegistry > xTempReg;
            xEnum->nextElement() >>= xTempReg;
            if( xTempReg.is() )
            {
                const Sequence< OUString > seq2 = retrieveAsciiValueList( xTempReg, keyName );
 
                if( seq2.hasElements() )
                {
                    sal_Int32 n1Len = seq.getLength();
                    sal_Int32 n2Len = seq2.getLength();
 
                    seq.realloc( n1Len + n2Len );
                    std::copy(seq2.begin(), seq2.end(), std::next(seq.getArray(), n1Len));
                }
            }
        }
    }
    else if( xReg.is () )
    {
        try
        {
            Reference< XRegistryKey > rRootKey = xReg->getRootKey();
            if( rRootKey.is() )
            {
                Reference<XRegistryKey > xKey = rRootKey->openKey(keyName);
                if( xKey.is() )
                {
                    seq = xKey->getAsciiListValue();
                }
            }
        }
        catch( InvalidRegistryException & )
        {
        }
        catch (InvalidValueException &)
        {
        }
    }
    return seq;
}
 
/*****************************************************************************
    Enumeration by ServiceName
*****************************************************************************/
 
typedef std::unordered_set< Reference<XInterface > > HashSet_Ref;
 
 
class ServiceEnumeration_Impl : public WeakImplHelper< XEnumeration >
{
public:
    explicit ServiceEnumeration_Impl( const Sequence< Reference<XInterface > > & rFactories )
        : aFactories( rFactories )
        , nIt( 0 )
        {}
 
    // XEnumeration
    sal_Bool SAL_CALL hasMoreElements() override;
    Any SAL_CALL nextElement() override;
private:
    std::mutex                          aMutex;
    Sequence< Reference<XInterface > >  aFactories;
    sal_Int32                           nIt;
};
 
// XEnumeration
sal_Bool ServiceEnumeration_Impl::hasMoreElements()
{
    std::scoped_lock aGuard( aMutex );
    return nIt != aFactories.getLength();
}
 
// XEnumeration
Any ServiceEnumeration_Impl::nextElement()
{
    std::scoped_lock aGuard( aMutex );
    if( nIt == aFactories.getLength() )
        throw NoSuchElementException(u"no more elements"_ustr);
 
    return Any( &aFactories[nIt++], cppu::UnoType<XInterface>::get());
}
 
 
class PropertySetInfo_Impl : public WeakImplHelper< beans::XPropertySetInfo >
{
    Sequence< beans::Property > m_properties;
 
public:
    explicit PropertySetInfo_Impl( Sequence< beans::Property > const & properties )
        : m_properties( properties )
        {}
 
    // XPropertySetInfo impl
    virtual Sequence< beans::Property > SAL_CALL getProperties() override;
    virtual beans::Property SAL_CALL getPropertyByName( OUString const & name ) override;
    virtual sal_Bool SAL_CALL hasPropertyByName( OUString const & name ) override;
};
 
Sequence< beans::Property > PropertySetInfo_Impl::getProperties()
{
    return m_properties;
}
 
beans::Property PropertySetInfo_Impl::getPropertyByName( OUString const & name )
{
    for ( sal_Int32 nPos = m_properties.getLength(); nPos--; )
    {
        if (m_properties[nPos].Name == name)
            return m_properties[nPos];
    }
    throw beans::UnknownPropertyException(
        "unknown property: " + name );
}
 
sal_Bool PropertySetInfo_Impl::hasPropertyByName( OUString const & name )
{
    return std::any_of(std::cbegin(m_properties), std::cend(m_properties),
        [&name](const beans::Property& rProp) { return rProp.Name == name; });
}
 
 
/*****************************************************************************
    Enumeration by implementation
*****************************************************************************/
class ImplementationEnumeration_Impl : public WeakImplHelper< XEnumeration >
{
public:
    explicit ImplementationEnumeration_Impl( HashSet_Ref xImplementationMap )
        : aImplementationMap(std::move( xImplementationMap ))
        , aIt( aImplementationMap.begin() )
        {}
 
    // XEnumeration
    virtual sal_Bool SAL_CALL hasMoreElements() override;
    virtual Any SAL_CALL nextElement() override;
 
private:
    std::mutex                      aMutex;
    HashSet_Ref                     aImplementationMap;
    HashSet_Ref::iterator           aIt;
};
 
// XEnumeration
sal_Bool ImplementationEnumeration_Impl::hasMoreElements()
{
    std::scoped_lock aGuard( aMutex );
    return aIt != aImplementationMap.end();
}
 
// XEnumeration
Any ImplementationEnumeration_Impl::nextElement()
{
    std::scoped_lock aGuard( aMutex );
    if( aIt == aImplementationMap.end() )
        throw NoSuchElementException(u"no more elements"_ustr);
 
    Any ret( &(*aIt), cppu::UnoType<XInterface>::get());
    ++aIt;
    return ret;
}
 
/*****************************************************************************
    Hash tables
*****************************************************************************/
typedef std::unordered_set
<
    OUString
> HashSet_OWString;
 
typedef std::unordered_multimap
<
    OUString,
    Reference<XInterface >
> HashMultimap_OWString_Interface;
 
typedef std::unordered_map
<
    OUString,
    Reference<XInterface >
> HashMap_OWString_Interface;
 
/*****************************************************************************
    class OServiceManager_Listener
*****************************************************************************/
class OServiceManager_Listener : public WeakImplHelper< XEventListener >
{
private:
    WeakReference<XSet > xSMgr;
 
public:
    explicit OServiceManager_Listener( const Reference<XSet > & rSMgr )
        : xSMgr( rSMgr )
        {}
 
    // XEventListener
    virtual void SAL_CALL disposing(const EventObject & rEvt ) override;
};
 
void OServiceManager_Listener::disposing(const EventObject & rEvt )
{
    Reference<XSet > x( xSMgr );
    if( !x.is() )
        return;
 
    try
    {
        x->remove( Any( &rEvt.Source, cppu::UnoType<XInterface>::get()) );
    }
    catch( const IllegalArgumentException & )
    {
        OSL_FAIL( "IllegalArgumentException caught" );
    }
    catch( const NoSuchElementException & )
    {
        OSL_FAIL( "NoSuchElementException caught" );
    }
}
 
 
/*****************************************************************************
    class OServiceManager
*****************************************************************************/
 
typedef WeakComponentImplHelper<
    lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo,
    lang::XInitialization,
    container::XSet, container::XContentEnumerationAccess,
    beans::XPropertySet > t_OServiceManager_impl;
 
class OServiceManager
    : public cppu::BaseMutex
    , public t_OServiceManager_impl
{
public:
    explicit OServiceManager( Reference< XComponentContext > const & xContext );
 
    // XInitialization
    void SAL_CALL initialize( Sequence< Any > const & args ) override;
 
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override;
    virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
 
    // XMultiComponentFactory
    virtual Reference< XInterface > SAL_CALL createInstanceWithContext(
        OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override;
    virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext(
        OUString const & rServiceSpecifier,
        Sequence< Any > const & rArguments,
        Reference< XComponentContext > const & xContext ) override;
//      virtual Sequence< OUString > SAL_CALL getAvailableServiceNames()
//          throw (RuntimeException);
 
    // XMultiServiceFactory
    virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
    virtual Reference<XInterface > SAL_CALL createInstance(const OUString &) override;
    virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString &, const Sequence<Any >& Arguments) override;
 
    // The same as the getAvailableServiceNames, but only unique names
    Sequence< OUString > getUniqueAvailableServiceNames(
        HashSet_OWString & aNameSet );
 
    // XElementAccess
    virtual Type SAL_CALL getElementType() override;
    virtual sal_Bool SAL_CALL hasElements() override;
 
    // XEnumerationAccess
    virtual Reference<XEnumeration > SAL_CALL createEnumeration() override;
 
    // XSet
    virtual sal_Bool SAL_CALL has( const Any & Element ) override;
    virtual void SAL_CALL insert( const Any & Element ) override;
    virtual void SAL_CALL remove( const Any & Element ) override;
 
    // XContentEnumerationAccess
    //Sequence< OUString >          getAvailableServiceNames() throw( (Exception) );
    virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override;
 
    // XComponent
    virtual void SAL_CALL dispose() override;
 
    // XPropertySet
    Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
    void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override;
    Any SAL_CALL getPropertyValue(const OUString& PropertyName) override;
    void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override;
    void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override;
    void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override;
    void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override;
 
protected:
    bool is_disposed() const;
    void check_undisposed() const;
    virtual void SAL_CALL disposing() override;
 
    bool haveFactoryWithThisImplementation(const OUString& aImplName);
 
    virtual Sequence< Reference< XInterface > > queryServiceFactories(
        const OUString& aServiceName, Reference< XComponentContext > const & xContext );
 
    Reference< XComponentContext >  m_xContext;
 
    Reference< beans::XPropertySetInfo > m_xPropertyInfo;
 
    // factories which have been loaded and not inserted( by XSet::insert)
    // are remembered by this set.
    HashSet_Ref m_SetLoadedFactories;
private:
 
    Reference<XEventListener >      getFactoryListener();
 
 
    HashMultimap_OWString_Interface m_ServiceMap;
    HashSet_Ref                     m_ImplementationMap;
    HashMap_OWString_Interface      m_ImplementationNameMap;
    rtl::Reference<OServiceManager_Listener > xFactoryListener;
    bool                            m_bInDisposing;
};
 
 
bool OServiceManager::is_disposed() const
{
    // ought to be guarded by m_mutex:
    return (m_bInDisposing || rBHelper.bDisposed);
}
 
 
void OServiceManager::check_undisposed() const
{
    if (is_disposed())
    {
        throw lang::DisposedException(
            u"service manager instance has already been disposed!"_ustr,
            const_cast<OServiceManager *>(this)->getXWeak() );
    }
}
 
 
typedef WeakComponentImplHelper<
    lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo,
    container::XSet, container::XContentEnumerationAccess,
    beans::XPropertySet > t_OServiceManagerWrapper_impl;
 
class OServiceManagerWrapper : public cppu::BaseMutex, public t_OServiceManagerWrapper_impl
{
    Reference< XComponentContext > m_xContext;
    Reference< XMultiComponentFactory > m_root;
    Reference< XMultiComponentFactory > const & getRoot() const
    {
        if (! m_root.is())
        {
            throw lang::DisposedException(
                u"service manager instance has already been disposed!"_ustr );
        }
        return m_root;
    }
 
protected:
    virtual void SAL_CALL disposing() override;
 
public:
    explicit OServiceManagerWrapper(
        Reference< XComponentContext > const & xContext );
 
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override
        { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getImplementationName(); }
    virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override
        { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->supportsService( ServiceName ); }
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override
        { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getSupportedServiceNames(); }
 
    // XMultiComponentFactory
    virtual Reference< XInterface > SAL_CALL createInstanceWithContext(
        OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override
        { return getRoot()->createInstanceWithContext( rServiceSpecifier, xContext ); }
    virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext(
        OUString const & rServiceSpecifier,
        Sequence< Any > const & rArguments,
        Reference< XComponentContext > const & xContext ) override
        { return getRoot()->createInstanceWithArgumentsAndContext( rServiceSpecifier, rArguments, xContext ); }
//      virtual Sequence< OUString > SAL_CALL getAvailableServiceNames()
//          throw (RuntimeException);
 
    // XMultiServiceFactory
    virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override
        { return getRoot()->getAvailableServiceNames(); }
    virtual Reference<XInterface > SAL_CALL createInstance(const OUString & name) override
        { return getRoot()->createInstanceWithContext( name, m_xContext ); }
    virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString & name, const Sequence<Any >& Arguments) override
        { return getRoot()->createInstanceWithArgumentsAndContext( name, Arguments, m_xContext ); }
 
    // XElementAccess
    virtual Type SAL_CALL getElementType() override
        { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->getElementType(); }
    virtual sal_Bool SAL_CALL hasElements() override
        { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->hasElements(); }
 
    // XEnumerationAccess
    virtual Reference<XEnumeration > SAL_CALL createEnumeration() override
        { return Reference< XEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createEnumeration(); }
 
    // XSet
    virtual sal_Bool SAL_CALL has( const Any & Element ) override
        { return Reference< XSet >(getRoot(), UNO_QUERY_THROW)->has( Element ); }
    virtual void SAL_CALL insert( const Any & Element ) override
        { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->insert( Element ); }
    virtual void SAL_CALL remove( const Any & Element ) override
        { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->remove( Element ); }
 
    // XContentEnumerationAccess
    //Sequence< OUString >          getAvailableServiceNames() throw( (Exception) );
    virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override
        { return Reference< XContentEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createContentEnumeration( aServiceName ); }
 
    // XPropertySet
    Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override
        { return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertySetInfo(); }
 
    void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override;
    Any SAL_CALL getPropertyValue(const OUString& PropertyName) override;
 
    void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override
        { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addPropertyChangeListener( PropertyName, aListener ); }
    void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override
        { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removePropertyChangeListener( PropertyName, aListener ); }
    void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override
        { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addVetoableChangeListener( PropertyName, aListener ); }
    void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override
        { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removeVetoableChangeListener( PropertyName, aListener ); }
};
 
void SAL_CALL OServiceManagerWrapper::setPropertyValue(
    const OUString& PropertyName, const Any& aValue )
{
    if ( PropertyName == "DefaultContext" )
    {
        Reference< XComponentContext > xContext;
        if (!(aValue >>= xContext))
        {
            throw IllegalArgumentException(
                u"no XComponentContext given!"_ustr,
                getXWeak(), 1 );
        }
 
        MutexGuard aGuard( m_aMutex );
        m_xContext = std::move(xContext);
 
    }
    else
    {
        Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->setPropertyValue( PropertyName, aValue );
    }
}
 
Any SAL_CALL OServiceManagerWrapper::getPropertyValue(
    const OUString& PropertyName )
{
    if ( PropertyName == "DefaultContext" )
    {
        MutexGuard aGuard( m_aMutex );
        if( m_xContext.is() )
            return Any( m_xContext );
        else
            return Any();
    }
    else
    {
        return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertyValue( PropertyName );
    }
}
 
void OServiceManagerWrapper::disposing()
{
    m_xContext.clear();
 
// no m_root->dispose(), because every context disposes its service manager...
    m_root.clear();
}
 
OServiceManagerWrapper::OServiceManagerWrapper(
    Reference< XComponentContext > const & xContext )
    : t_OServiceManagerWrapper_impl( m_aMutex )
    , m_xContext( xContext )
    , m_root( xContext->getServiceManager() )
{
    if (! m_root.is())
    {
        throw RuntimeException(
            u"no service manager to wrap"_ustr );
    }
}
 
 
/**
 * Create a ServiceManager
 */
OServiceManager::OServiceManager( Reference< XComponentContext > const & xContext )
    : t_OServiceManager_impl( m_aMutex )
    , m_xContext( xContext )
    , m_bInDisposing( false )
{}
 
// XComponent
void OServiceManager::dispose()
{
    if (rBHelper.bDisposed || rBHelper.bInDispose)
        return;
    t_OServiceManager_impl::dispose();
}
 
void OServiceManager::disposing()
{
    // dispose all factories
    HashSet_Ref aImpls;
    {
        MutexGuard aGuard( m_aMutex );
        m_bInDisposing = true;
        aImpls = m_ImplementationMap;
    }
    for( const auto& rxImpl : aImpls )
    {
        try
        {
            Reference<XComponent > xComp( Reference<XComponent >::query( rxImpl ) );
            if( xComp.is() )
                xComp->dispose();
        }
        catch (const RuntimeException & exc)
        {
            SAL_INFO("stoc", "RuntimeException occurred upon disposing factory: " << exc);
        }
    }
 
    // dispose
    HashSet_Ref aImplMap;
    {
        MutexGuard aGuard( m_aMutex );
        // erase all members
        m_ServiceMap = HashMultimap_OWString_Interface();
        aImplMap = m_ImplementationMap;
        m_ImplementationMap = HashSet_Ref();
        m_ImplementationNameMap = HashMap_OWString_Interface();
        m_SetLoadedFactories= HashSet_Ref();
    }
 
    m_xContext.clear();
 
    // not only the Event should hold the object
    OSL_ASSERT( m_refCount != 1 );
}
 
// XPropertySet
Reference<XPropertySetInfo > OServiceManager::getPropertySetInfo()
{
    check_undisposed();
    if (! m_xPropertyInfo.is())
    {
        Sequence< beans::Property > seq{ beans::Property(
            u"DefaultContext"_ustr, -1, cppu::UnoType<decltype(m_xContext)>::get(), 0 ) };
        Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) );
 
        MutexGuard aGuard( m_aMutex );
        if (! m_xPropertyInfo.is())
        {
            m_xPropertyInfo = std::move(xInfo);
        }
    }
    return m_xPropertyInfo;
}
 
void OServiceManager::setPropertyValue(
    const OUString& PropertyName, const Any& aValue )
{
    check_undisposed();
    if ( PropertyName != "DefaultContext" )
    {
        throw UnknownPropertyException(
            "unknown property " + PropertyName,
            getXWeak() );
    }
 
    Reference< XComponentContext > xContext;
    if (!(aValue >>= xContext))
    {
        throw IllegalArgumentException(
            u"no XComponentContext given!"_ustr,
            getXWeak(), 1 );
    }
 
    MutexGuard aGuard( m_aMutex );
    m_xContext = std::move(xContext);
}
 
Any OServiceManager::getPropertyValue(const OUString& PropertyName)
{
    check_undisposed();
    if ( PropertyName == "DefaultContext" )
    {
        MutexGuard aGuard( m_aMutex );
        if( m_xContext.is() )
            return Any( m_xContext );
        else
            return Any();
    }
    else
    {
        UnknownPropertyException except("ServiceManager : unknown property " + PropertyName, {});
        throw except;
    }
}
 
void OServiceManager::addPropertyChangeListener(
    const OUString&, const Reference<XPropertyChangeListener >&)
{
    check_undisposed();
    throw UnknownPropertyException(u"unsupported"_ustr);
}
 
void OServiceManager::removePropertyChangeListener(
    const OUString&, const Reference<XPropertyChangeListener >&)
{
    check_undisposed();
    throw UnknownPropertyException(u"unsupported"_ustr);
}
 
void OServiceManager::addVetoableChangeListener(
    const OUString&, const Reference<XVetoableChangeListener >&)
{
    check_undisposed();
    throw UnknownPropertyException(u"unsupported"_ustr);
}
 
void OServiceManager::removeVetoableChangeListener(
    const OUString&, const Reference<XVetoableChangeListener >&)
{
    check_undisposed();
    throw UnknownPropertyException(u"unsupported"_ustr);
}
 
// OServiceManager
Reference<XEventListener > OServiceManager::getFactoryListener()
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    if( !xFactoryListener.is() )
        xFactoryListener = new OServiceManager_Listener( this );
    return xFactoryListener;
}
 
// XMultiServiceFactory, XContentEnumeration
Sequence< OUString > OServiceManager::getUniqueAvailableServiceNames(
    HashSet_OWString & aNameSet )
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    for( const auto& rEntry : m_ServiceMap )
        aNameSet.insert( rEntry.first );
 
    /* do not return the implementation names
    HashMap_OWString_Interface      m_ImplementationNameMap;
    HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.begin();
    while( aIt != m_ImplementationNameMap.end() )
        aNameSet.insert( (*aIt++).first );
    */
 
    return comphelper::containerToSequence(aNameSet);
}
 
// XMultiComponentFactory
Reference< XInterface > OServiceManager::createInstanceWithContext(
    OUString const & rServiceSpecifier,
    Reference< XComponentContext > const & xContext )
{
    check_undisposed();
#if OSL_DEBUG_LEVEL > 0
    Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY );
    OSL_ASSERT( xProps.is() );
    if (xProps.is())
    {
        Reference< XComponentContext > xDefContext;
        xProps->getPropertyValue( u"DefaultContext"_ustr ) >>= xDefContext;
        OSL_ENSURE(
            xContext == xDefContext,
            "### default context of service manager singleton differs from context holding it!" );
    }
#endif
 
    const Sequence< Reference< XInterface > > factories(
        queryServiceFactories( rServiceSpecifier, xContext ) );
    for ( Reference< XInterface > const & xFactory : factories )
    {
        try
        {
            if (xFactory.is())
            {
                Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY );
                if (xFac.is())
                {
                    return xFac->createInstanceWithContext( xContext );
                }
                else
                {
                    Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY );
                    if (xFac2.is())
                    {
                        SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!");
                        return xFac2->createInstance();
                    }
                }
            }
        }
        catch (const lang::DisposedException & exc)
        {
            SAL_INFO("stoc", "DisposedException occurred: " << exc);
        }
    }
 
    return Reference< XInterface >();
}
// XMultiComponentFactory
Reference< XInterface > OServiceManager::createInstanceWithArgumentsAndContext(
    OUString const & rServiceSpecifier,
    Sequence< Any > const & rArguments,
    Reference< XComponentContext > const & xContext )
{
    check_undisposed();
#if OSL_DEBUG_LEVEL > 0
    Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY );
    OSL_ASSERT( xProps.is() );
    if (xProps.is())
    {
        Reference< XComponentContext > xDefContext;
        xProps->getPropertyValue( u"DefaultContext"_ustr ) >>= xDefContext;
        OSL_ENSURE(
            xContext == xDefContext,
            "### default context of service manager singleton differs from context holding it!" );
    }
#endif
 
    const Sequence< Reference< XInterface > > factories(
        queryServiceFactories( rServiceSpecifier, xContext ) );
    for ( Reference< XInterface > const & xFactory : factories )
    {
        try
        {
            if (xFactory.is())
            {
                Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY );
                if (xFac.is())
                {
                    return xFac->createInstanceWithArgumentsAndContext( rArguments, xContext );
                }
                else
                {
                    Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY );
                    if (xFac2.is())
                    {
                        SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!");
                        return xFac2->createInstanceWithArguments( rArguments );
                    }
                }
            }
        }
        catch (const lang::DisposedException & exc)
        {
            SAL_INFO("stoc", "DisposedException occurred: " << exc);
        }
    }
 
    return Reference< XInterface >();
}
 
// XMultiServiceFactory, XMultiComponentFactory, XContentEnumeration
Sequence< OUString > OServiceManager::getAvailableServiceNames()
{
    check_undisposed();
    // all names
    HashSet_OWString aNameSet;
    return getUniqueAvailableServiceNames( aNameSet );
}
 
// XMultipleServiceFactory
Reference<XInterface > OServiceManager::createInstance(
    const OUString& rServiceSpecifier )
{
    return createInstanceWithContext(
        rServiceSpecifier, m_xContext );
}
 
// XMultipleServiceFactory
Reference<XInterface > OServiceManager::createInstanceWithArguments(
    const OUString& rServiceSpecifier,
    const Sequence<Any >& rArguments )
{
    return createInstanceWithArgumentsAndContext(
        rServiceSpecifier, rArguments, m_xContext );
}
 
// XInitialization
void OServiceManager::initialize( Sequence< Any > const & )
{
    check_undisposed();
    OSL_FAIL( "not impl!" );
}
 
// XServiceInfo
OUString OServiceManager::getImplementationName()
{
    return u"com.sun.star.comp.stoc.OServiceManager"_ustr;
}
 
// XServiceInfo
sal_Bool OServiceManager::supportsService(const OUString& ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}
 
// XServiceInfo
Sequence< OUString > OServiceManager::getSupportedServiceNames()
{
    return { u"com.sun.star.lang.MultiServiceFactory"_ustr, u"com.sun.star.lang.ServiceManager"_ustr };
}
 
 
Sequence< Reference< XInterface > > OServiceManager::queryServiceFactories(
    const OUString& aServiceName, Reference< XComponentContext > const & )
{
    Sequence< Reference< XInterface > > ret;
 
    MutexGuard aGuard( m_aMutex );
    ::std::pair<
          HashMultimap_OWString_Interface::iterator,
          HashMultimap_OWString_Interface::iterator> p(
              m_ServiceMap.equal_range( aServiceName ) );
 
    if (p.first == p.second) // no factories
    {
        // no service found, look for an implementation
        HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.find( aServiceName );
        if( aIt != m_ImplementationNameMap.end() )
        {
            Reference< XInterface > const & x = aIt->second;
            // an implementation found
            ret = Sequence< Reference< XInterface > >( &x, 1 );
        }
    }
    else
    {
        ::std::vector< Reference< XInterface > > vec;
        vec.reserve( 4 );
        while (p.first != p.second)
        {
            vec.push_back( p.first->second );
            ++p.first;
        }
        ret = Sequence< Reference< XInterface > >( vec.data(), vec.size() );
    }
 
    return ret;
}
 
// XContentEnumerationAccess
Reference<XEnumeration > OServiceManager::createContentEnumeration(
    const OUString& aServiceName )
{
    check_undisposed();
    Sequence< Reference< XInterface > > factories(
        OServiceManager::queryServiceFactories( aServiceName, m_xContext ) );
    if (factories.hasElements())
        return new ServiceEnumeration_Impl( factories );
    else
        return Reference< XEnumeration >();
}
 
// XEnumeration
Reference<XEnumeration > OServiceManager::createEnumeration()
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    return new ImplementationEnumeration_Impl( m_ImplementationMap );
}
 
// XElementAccess
Type OServiceManager::getElementType()
{
    check_undisposed();
    return cppu::UnoType<XInterface>::get();
}
 
// XElementAccess
sal_Bool OServiceManager::hasElements()
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    return !m_ImplementationMap.empty();
}
 
// XSet
sal_Bool OServiceManager::has( const Any & Element )
{
    check_undisposed();
    if( Element.getValueTypeClass() == TypeClass_INTERFACE )
    {
        Reference<XInterface > xEle( Element, UNO_QUERY_THROW );
        MutexGuard aGuard( m_aMutex );
        return m_ImplementationMap.find( xEle ) !=
            m_ImplementationMap.end();
    }
    else if (auto implName = o3tl::tryAccess<OUString>(Element))
    {
        MutexGuard aGuard( m_aMutex );
        return m_ImplementationNameMap.find( *implName ) !=
            m_ImplementationNameMap.end();
    }
    return false;
}
 
// XSet
void OServiceManager::insert( const Any & Element )
{
    check_undisposed();
    if( Element.getValueTypeClass() != TypeClass_INTERFACE )
    {
        throw IllegalArgumentException(
            "exception interface, got " + Element.getValueTypeName(),
            Reference< XInterface >(), 0 );
    }
    Reference<XInterface > xEle( Element, UNO_QUERY_THROW );
 
    {
    MutexGuard aGuard( m_aMutex );
    HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle );
    if( aIt != m_ImplementationMap.end() )
    {
        throw ElementExistException( u"element already exists!"_ustr );
    }
 
    // put into the implementation hashmap
    m_ImplementationMap.insert( xEle );
 
    // put into the implementation name hashmap
    Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) );
    if( xInfo.is() )
    {
        OUString aImplName = xInfo->getImplementationName();
        if( !aImplName.isEmpty() )
            m_ImplementationNameMap[ aImplName ] = xEle;
 
        //put into the service map
        const Sequence< OUString > aServiceNames = xInfo->getSupportedServiceNames();
        for( const OUString& rServiceName : aServiceNames )
        {
            m_ServiceMap.emplace(
                rServiceName, *o3tl::doAccess<Reference<XInterface>>(Element) );
        }
    }
    }
    // add the disposing listener to the factory
    Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) );
    if( xComp.is() )
        xComp->addEventListener( getFactoryListener() );
}
 
// helper function
bool OServiceManager::haveFactoryWithThisImplementation(const OUString& aImplName)
{
    return ( m_ImplementationNameMap.find(aImplName) != m_ImplementationNameMap.end());
}
 
// XSet
void OServiceManager::remove( const Any & Element )
{
    if (is_disposed())
        return;
 
    Reference<XInterface > xEle;
    if (Element.getValueTypeClass() == TypeClass_INTERFACE)
    {
        xEle.set( Element, UNO_QUERY_THROW );
    }
    else if (auto implName = o3tl::tryAccess<OUString>(Element))
    {
        MutexGuard aGuard( m_aMutex );
        HashMap_OWString_Interface::const_iterator const iFind(
            m_ImplementationNameMap.find( *implName ) );
        if (iFind == m_ImplementationNameMap.end())
        {
            throw NoSuchElementException(
                "element is not in: " + *implName,
                getXWeak() );
        }
        xEle = iFind->second;
    }
    else
    {
        throw IllegalArgumentException(
            "expected interface or string, got " + Element.getValueTypeName(),
            Reference< XInterface >(), 0 );
    }
 
    // remove the disposing listener from the factory
    Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) );
    if( xComp.is() )
        xComp->removeEventListener( getFactoryListener() );
 
    MutexGuard aGuard( m_aMutex );
    HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle );
    if( aIt == m_ImplementationMap.end() )
    {
        throw NoSuchElementException(
            u"element not found"_ustr,
            getXWeak() );
    }
    //First remove all factories which have been loaded by ORegistryServiceManager.
    m_SetLoadedFactories.erase( *aIt);
    //Remove from the implementation map. It contains all factories of m_SetLoadedFactories
    //which have been added directly through XSet, that is not via ORegistryServiceManager
    m_ImplementationMap.erase( aIt );
 
    // remove from the implementation name hashmap
    Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) );
    if( xInfo.is() )
    {
        OUString aImplName = xInfo->getImplementationName();
        if( !aImplName.isEmpty() )
            m_ImplementationNameMap.erase( aImplName );
    }
 
    //remove from the service map
    Reference<XServiceInfo > xSF( Reference<XServiceInfo >::query( xEle ) );
    if( !xSF.is() )
        return;
 
    const Sequence< OUString > aServiceNames = xSF->getSupportedServiceNames();
    for( const OUString& rServiceName : aServiceNames )
    {
        std::pair<HashMultimap_OWString_Interface::iterator, HashMultimap_OWString_Interface::iterator> p =
            m_ServiceMap.equal_range( rServiceName );
 
        while( p.first != p.second )
        {
            if( xEle == (*p.first).second )
            {
                m_ServiceMap.erase( p.first );
                break;
            }
            ++p.first;
        }
    }
}
 
/*****************************************************************************
    class ORegistryServiceManager
*****************************************************************************/
class ORegistryServiceManager : public OServiceManager
{
public:
    explicit ORegistryServiceManager( Reference< XComponentContext > const & xContext );
 
    // XInitialization
    void SAL_CALL initialize(const Sequence< Any >& Arguments) override;
 
    // XServiceInfo
    OUString SAL_CALL getImplementationName() override
        { return u"com.sun.star.comp.stoc.ORegistryServiceManager"_ustr; }
 
    Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
 
    // XMultiServiceFactory
    Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
 
    // XContentEnumerationAccess
    //Sequence< OUString >          getAvailableServiceNames() throw( (Exception) );
    Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override;
 
    // XComponent
    void SAL_CALL dispose() override;
 
    // OServiceManager
    Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
    Any SAL_CALL getPropertyValue(const OUString& PropertyName) override;
 
protected:
    //OServiceManager
    Sequence< Reference< XInterface > > queryServiceFactories(
        const OUString& aServiceName, Reference< XComponentContext > const & xContext ) override;
private:
    Reference<XRegistryKey >        getRootKey();
    Reference<XInterface > loadWithImplementationName(
        const OUString & rImplName, Reference< XComponentContext > const & xContext );
    Sequence<OUString>          getFromServiceName(std::u16string_view serviceName) const;
    Reference<XInterface > loadWithServiceName(
        std::u16string_view rImplName, Reference< XComponentContext > const & xContext );
    void                        fillAllNamesFromRegistry( HashSet_OWString & );
 
    bool                    m_searchedRegistry;
    Reference<XSimpleRegistry > m_xRegistry;    // readonly property Registry
    Reference<XRegistryKey >    m_xRootKey;
 
#if OSL_DEBUG_LEVEL > 0
    bool m_init;
#endif
};
 
/**
 * Create a ServiceManager
 */
ORegistryServiceManager::ORegistryServiceManager( Reference< XComponentContext > const & xContext )
    : OServiceManager( xContext )
    , m_searchedRegistry(false)
#if OSL_DEBUG_LEVEL > 0
    , m_init( false )
#endif
{
}
 
// XComponent
void ORegistryServiceManager::dispose()
{
    if (rBHelper.bDisposed || rBHelper.bInDispose)
        return;
    OServiceManager::dispose();
    // dispose
    MutexGuard aGuard( m_aMutex );
    // erase all members
    m_xRegistry.clear();
    m_xRootKey.clear();
}
 
/**
 * Return the root key of the registry. The Default registry service is ordered
 * if no registry is set.
 */
//Reference<XServiceProvider > create_DefaultRegistry_ServiceProvider();
 
Reference<XRegistryKey > ORegistryServiceManager::getRootKey()
{
    if( !m_xRootKey.is() )
    {
        MutexGuard aGuard( m_aMutex );
        //  DefaultRegistry suchen !!!!
        if( !m_xRegistry.is() && !m_searchedRegistry )
        {
            // NB. we only search this once
            m_searchedRegistry = true;
 
            m_xRegistry.set(
                createInstanceWithContext(
                    u"com.sun.star.registry.DefaultRegistry"_ustr,
                    m_xContext ),
                UNO_QUERY );
        }
        if( m_xRegistry.is() && !m_xRootKey.is() )
            m_xRootKey = m_xRegistry->getRootKey();
    }
 
    return m_xRootKey;
}
 
/**
 * Create a service provider from the registry with an implementation name
 */
Reference<XInterface > ORegistryServiceManager::loadWithImplementationName(
    const OUString& name, Reference< XComponentContext > const & xContext )
{
    Reference<XInterface > ret;
 
    Reference<XRegistryKey > xRootKey = getRootKey();
    if( !xRootKey.is() )
        return ret;
 
    try
    {
        OUString implementationName = "/IMPLEMENTATIONS/" + name;
        Reference<XRegistryKey > xImpKey = m_xRootKey->openKey(implementationName);
 
        if( xImpKey.is() )
        {
            Reference< lang::XMultiServiceFactory > xMgr;
            if (xContext.is())
                xMgr.set( xContext->getServiceManager(), UNO_QUERY_THROW );
            else
                xMgr.set( this );
            ret = createSingleRegistryFactory( xMgr, name, xImpKey );
            insert( Any( ret ) );
            // Remember this factory as loaded in contrast to inserted ( XSet::insert)
            // factories. Those loaded factories in this set are candidates for being
            // released on an unloading notification.
            m_SetLoadedFactories.insert( ret);
        }
    }
    catch (InvalidRegistryException &)
    {
    }
 
    return ret;
}
 
/**
 * Return all implementation out of the registry.
 */
Sequence<OUString> ORegistryServiceManager::getFromServiceName(
    std::u16string_view serviceName ) const
{
    OUString buf = OUString::Concat("/SERVICES/") + serviceName;
    return retrieveAsciiValueList( m_xRegistry, buf );
}
 
/**
 * Create a service provider from the registry
 */
Reference<XInterface > ORegistryServiceManager::loadWithServiceName(
    std::u16string_view serviceName, Reference< XComponentContext > const & xContext )
{
    const Sequence<OUString> implEntries = getFromServiceName( serviceName );
    for (const auto& rEntry : implEntries)
    {
        Reference< XInterface > x( loadWithImplementationName( rEntry, xContext ) );
        if (x.is())
            return x;
    }
 
    return Reference<XInterface >();
}
 
/**
 * Return a sequence of all service names from the registry.
 */
void ORegistryServiceManager::fillAllNamesFromRegistry( HashSet_OWString & rSet )
{
    Reference<XRegistryKey > xRootKey = getRootKey();
    if( !xRootKey.is() )
        return;
 
    try
    {
        Reference<XRegistryKey > xServicesKey = xRootKey->openKey( u"SERVICES"_ustr );
        // root + /Services + /
        if( xServicesKey.is() )
        {
            sal_Int32 nPrefix = xServicesKey->getKeyName().getLength() +1;
            const Sequence<Reference<XRegistryKey > > aKeys = xServicesKey->openKeys();
            std::transform(aKeys.begin(), aKeys.end(), std::inserter(rSet, rSet.end()),
                [nPrefix](const Reference<XRegistryKey>& rKey) -> OUString {
                    return rKey->getKeyName().copy( nPrefix ); });
        }
    }
    catch (InvalidRegistryException &)
    {
    }
}
 
// XInitialization
void ORegistryServiceManager::initialize(const Sequence< Any >& Arguments)
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    if (Arguments.hasElements())
    {
        m_xRootKey.clear();
        Arguments[ 0 ] >>= m_xRegistry;
    }
#if OSL_DEBUG_LEVEL > 0
    // to find all bootstrapping processes to be fixed...
    OSL_ENSURE( !m_init, "### second init of service manager instance!" );
    m_init = true;
#endif
}
 
// XMultiServiceFactory, XContentEnumeration
Sequence< OUString > ORegistryServiceManager::getAvailableServiceNames()
{
    check_undisposed();
    MutexGuard aGuard( m_aMutex );
    // all names
    HashSet_OWString aNameSet;
 
    // all names from the registry
    fillAllNamesFromRegistry( aNameSet );
 
    return OServiceManager::getUniqueAvailableServiceNames( aNameSet );
}
 
// XServiceInfo
Sequence< OUString > ORegistryServiceManager::getSupportedServiceNames()
{
    return { u"com.sun.star.lang.MultiServiceFactory"_ustr, u"com.sun.star.lang.RegistryServiceManager"_ustr };
}
 
 
// OServiceManager
Sequence< Reference< XInterface > > ORegistryServiceManager::queryServiceFactories(
    const OUString& aServiceName, Reference< XComponentContext > const & xContext )
{
    Sequence< Reference< XInterface > > ret(
        OServiceManager::queryServiceFactories( aServiceName, xContext ) );
    if (ret.hasElements())
    {
        return ret;
    }
    else
    {
        MutexGuard aGuard( m_aMutex );
        Reference< XInterface > x( loadWithServiceName( aServiceName, xContext ) );
        if (! x.is())
            x = loadWithImplementationName( aServiceName, xContext );
        return Sequence< Reference< XInterface > >( &x, 1 );
    }
}
 
// XContentEnumerationAccess
Reference<XEnumeration > ORegistryServiceManager::createContentEnumeration(
    const OUString& aServiceName )
{
    check_undisposed();
    MutexGuard aGuard(m_aMutex);
    // get all implementation names registered under this service name from the registry
    const Sequence<OUString> aImpls = getFromServiceName( aServiceName );
    // load and insert all factories specified by the registry
    for( const OUString& aImplName : aImpls )
    {
        if ( !haveFactoryWithThisImplementation(aImplName) )
        {
            loadWithImplementationName( aImplName, m_xContext );
        }
    }
    // call the superclass to enumerate all contents
    return OServiceManager::createContentEnumeration( aServiceName );
}
 
// OServiceManager
Reference<XPropertySetInfo > ORegistryServiceManager::getPropertySetInfo()
{
    check_undisposed();
    if (! m_xPropertyInfo.is())
    {
        Sequence< beans::Property > seq{
            beans::Property(u"DefaultContext"_ustr, -1, cppu::UnoType<decltype(m_xContext)>::get(), 0),
            beans::Property(u"Registry"_ustr, -1, cppu::UnoType<decltype(m_xRegistry)>::get(),
                            beans::PropertyAttribute::READONLY)
        };
        Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) );
 
        MutexGuard aGuard( m_aMutex );
        if (! m_xPropertyInfo.is())
        {
            m_xPropertyInfo = std::move(xInfo);
        }
    }
    return m_xPropertyInfo;
}
 
Any ORegistryServiceManager::getPropertyValue(const OUString& PropertyName)
{
    check_undisposed();
    if ( PropertyName == "Registry" )
    {
        MutexGuard aGuard( m_aMutex );
        if( m_xRegistry.is() )
            return Any( m_xRegistry );
        else
            return Any();
    }
    return OServiceManager::getPropertyValue( PropertyName );
}
 
} // namespace
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_stoc_OServiceManager_get_implementation(
    css::uno::XComponentContext *context,
    css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new OServiceManager(context));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_stoc_ORegistryServiceManager_get_implementation(
    css::uno::XComponentContext *context,
    css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ORegistryServiceManager(context));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_stoc_OServiceManagerWrapper_get_implementation(
    css::uno::XComponentContext *context,
    css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new OServiceManagerWrapper(context));
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.