/* -*- 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 <unordered_map>
 
#include <osl/diagnose.h>
#include <osl/mutex.hxx>
 
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
 
#include <uno/lbnames.h>
#include <uno/mapping.hxx>
 
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/component_context.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/implbase.hxx>
 
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/uno/DeploymentException.hpp>
#include <com/sun/star/uno/RuntimeException.hpp>
 
#include <memory>
 
#define SMGR_SINGLETON "/singletons/com.sun.star.lang.theServiceManager"
#define TDMGR_SINGLETON "/singletons/com.sun.star.reflection.theTypeDescriptionManager"
#define AC_SINGLETON "/singletons/com.sun.star.security.theAccessController"
 
using namespace ::osl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
 
using rtl::OUString;
using rtl::OUStringBuffer;
 
namespace cppu
{
 
static inline void try_dispose( Reference< XInterface > const & xInstance )
{
    Reference< lang::XComponent > xComp( xInstance, UNO_QUERY );
    if (xComp.is())
    {
        xComp->dispose();
    }
}
 
static inline void try_dispose( Reference< lang::XComponent > const & xComp )
{
    if (xComp.is())
    {
        xComp->dispose();
    }
}
 
class DisposingForwarder
    : public WeakImplHelper< lang::XEventListener >
{
    Reference< lang::XComponent > m_xTarget;
 
    explicit DisposingForwarder( Reference< lang::XComponent > const & xTarget )
        : m_xTarget( xTarget )
    {
        OSL_ASSERT( m_xTarget.is() );
    }
public:
    // listens at source for disposing, then disposes target
    static inline void listen(
        Reference< lang::XComponent > const & xSource,
        Reference< lang::XComponent > const & xTarget );
 
    virtual void SAL_CALL disposing( lang::EventObject const & rSource ) override;
};
 
inline void DisposingForwarder::listen(
    Reference< lang::XComponent > const & xSource,
    Reference< lang::XComponent > const & xTarget )
{
    if (xSource.is())
    {
        xSource->addEventListener( new DisposingForwarder( xTarget ) );
    }
}
 
void DisposingForwarder::disposing( lang::EventObject const & )
{
    m_xTarget->dispose();
    m_xTarget.clear();
}
 
 
struct MutexHolder
{
protected:
    Mutex m_mutex;
};
 
 
class ComponentContext
    : private MutexHolder
    , public WeakComponentImplHelper< XComponentContext,
                                      container::XNameContainer >
{
protected:
    Reference< XComponentContext > m_xDelegate;
 
    struct ContextEntry
    {
        Any value;
        bool lateInit;
 
        ContextEntry( Any const & value_, bool lateInit_ )
            : value( value_ )
            , lateInit( lateInit_ )
            {}
    };
    typedef std::unordered_map< OUString, ContextEntry *  > t_map;
    t_map m_map;
 
    Reference< lang::XMultiComponentFactory > m_xSMgr;
 
protected:
    Any lookupMap( OUString const & rName );
 
    virtual void SAL_CALL disposing() override;
public:
    ComponentContext(
        ContextEntry_Init const * pEntries, sal_Int32 nEntries,
        Reference< XComponentContext > const & xDelegate );
    virtual ~ComponentContext() override;
 
    // XComponentContext
    virtual Any SAL_CALL getValueByName( OUString const & rName ) override;
    virtual Reference<lang::XMultiComponentFactory> SAL_CALL getServiceManager() override;
 
    // XNameContainer
    virtual void SAL_CALL insertByName(
        OUString const & name, Any const & element ) override;
    virtual void SAL_CALL removeByName( OUString const & name ) override;
    // XNameReplace
    virtual void SAL_CALL replaceByName(
        OUString const & name, Any const & element ) override;
    // XNameAccess
    virtual Any SAL_CALL getByName( OUString const & name ) override;
    virtual Sequence<OUString> SAL_CALL getElementNames() override;
    virtual sal_Bool SAL_CALL hasByName( OUString const & name ) override;
    // XElementAccess
    virtual Type SAL_CALL getElementType() override;
    virtual sal_Bool SAL_CALL hasElements() override;
};
 
// XNameContainer
 
void ComponentContext::insertByName(
    OUString const & name, Any const & element )
{
    t_map::mapped_type entry(
        new ContextEntry(
            element,
            /* lateInit_: */
            name.startsWith( "/singletons/" ) &&
            !element.hasValue() ) );
    MutexGuard guard( m_mutex );
    std::pair<t_map::iterator, bool> insertion( m_map.emplace(
        name, entry ) );
    if (! insertion.second)
        throw container::ElementExistException(
            "element already exists: " + name,
            static_cast<OWeakObject *>(this) );
}
 
 
void ComponentContext::removeByName( OUString const & name )
{
    MutexGuard guard( m_mutex );
    t_map::iterator iFind( m_map.find( name ) );
    if (iFind == m_map.end())
        throw container::NoSuchElementException(
            "no such element: " + name,
            static_cast<OWeakObject *>(this) );
 
    delete iFind->second;
    m_map.erase(iFind);
}
 
// XNameReplace
 
void ComponentContext::replaceByName(
    OUString const & name, Any const & element )
{
    MutexGuard guard( m_mutex );
    t_map::const_iterator const iFind( m_map.find( name ) );
    if (iFind == m_map.end())
        throw container::NoSuchElementException(
            "no such element: " + name,
            static_cast<OWeakObject *>(this) );
    if (name.startsWith( "/singletons/" ) &&
        !element.hasValue())
    {
        iFind->second->value.clear();
        iFind->second->lateInit = true;
    }
    else
    {
        iFind->second->value = element;
        iFind->second->lateInit = false;
    }
}
 
// XNameAccess
 
Any ComponentContext::getByName( OUString const & name )
{
    return getValueByName( name );
}
 
 
Sequence<OUString> ComponentContext::getElementNames()
{
    MutexGuard guard( m_mutex );
    Sequence<OUString> ret( m_map.size() );
    OUString * pret = ret.getArray();
    sal_Int32 pos = 0;
    t_map::const_iterator iPos( m_map.begin() );
    t_map::const_iterator const iEnd( m_map.end() );
    for ( ; iPos != iEnd; ++iPos )
        pret[pos++] = iPos->first;
    return ret;
}
 
 
sal_Bool ComponentContext::hasByName( OUString const & name )
{
    MutexGuard guard( m_mutex );
    return m_map.find( name ) != m_map.end();
}
 
// XElementAccess
 
Type ComponentContext::getElementType()
{
    return cppu::UnoType<void>::get();
}
 
 
sal_Bool ComponentContext::hasElements()
{
    MutexGuard guard( m_mutex );
    return ! m_map.empty();
}
 
 
Any ComponentContext::lookupMap( OUString const & rName )
{
    ResettableMutexGuard guard( m_mutex );
    t_map::const_iterator iFind( m_map.find( rName ) );
    if (iFind == m_map.end())
        return Any();
 
    t_map::mapped_type pEntry = iFind->second;
    if (! pEntry->lateInit)
        return pEntry->value;
 
    // late init singleton entry
    Reference< XInterface > xInstance;
    guard.clear();
 
    try
    {
        Any usesService( getValueByName( rName + "/service" ) );
        Any args_( getValueByName( rName + "/arguments" ) );
        Sequence<Any> args;
        if (args_.hasValue() && !(args_ >>= args))
        {
            args.realloc( 1 );
            args[ 0 ] = args_;
        }
 
        Reference< lang::XSingleComponentFactory > xFac;
        if (usesService >>= xFac) // try via factory
        {
            xInstance = args.getLength()
                ? xFac->createInstanceWithArgumentsAndContext( args, this )
                : xFac->createInstanceWithContext( this );
        }
        else
        {
            Reference< lang::XSingleServiceFactory > xFac2;
            if (usesService >>= xFac2)
            {
                // try via old XSingleServiceFactory
                xInstance = args.getLength()
                    ? xFac2->createInstanceWithArguments( args )
                    : xFac2->createInstance();
            }
            else if (m_xSMgr.is()) // optionally service name
            {
                OUString serviceName;
                if ((usesService >>= serviceName) &&
                    !serviceName.isEmpty())
                {
                    xInstance = args.getLength()
                        ? m_xSMgr->createInstanceWithArgumentsAndContext(
                            serviceName, args, this )
                        : m_xSMgr->createInstanceWithContext(
                            serviceName, this );
                }
            }
        }
    }
    catch (const RuntimeException &)
    {
        throw;
    }
    catch (const Exception & exc)
    {
        SAL_WARN(
            "cppuhelper",
            "exception occurred raising singleton \"" << rName << "\": "
            << exc);
    }
 
    SAL_WARN_IF(!xInstance.is(),
            "cppuhelper", "no service object raising singleton " << rName);
 
    Any ret;
    guard.reset();
    iFind = m_map.find( rName );
    if (iFind != m_map.end())
    {
        pEntry = iFind->second;
        if (pEntry->lateInit)
        {
            pEntry->value <<= xInstance;
            pEntry->lateInit = false;
            return pEntry->value;
        }
        ret = pEntry->value;
    }
    guard.clear();
    if (ret != xInstance) {
        try_dispose( xInstance );
    }
    return ret;
}
 
 
Any ComponentContext::getValueByName( OUString const & rName )
{
    // to determine the root context:
    if ( rName == "_root" )
    {
        if (m_xDelegate.is())
            return m_xDelegate->getValueByName( rName );
        return Any( Reference<XComponentContext>(this) );
    }
 
    Any ret( lookupMap( rName ) );
    if (!ret.hasValue() && m_xDelegate.is())
    {
        return m_xDelegate->getValueByName( rName );
    }
    return ret;
}
 
Reference< lang::XMultiComponentFactory > ComponentContext::getServiceManager()
{
    if ( !m_xSMgr.is() )
    {
        throw DeploymentException(
            "null component context service manager",
            static_cast<OWeakObject *>(this) );
    }
    return m_xSMgr;
}
 
ComponentContext::~ComponentContext()
{
    t_map::const_iterator iPos( m_map.begin() );
    t_map::const_iterator const iEnd( m_map.end() );
    for ( ; iPos != iEnd; ++iPos )
        delete iPos->second;
    m_map.clear();
}
 
void ComponentContext::disposing()
{
    Reference< lang::XComponent > xTDMgr, xAC; // to be disposed separately
 
    // dispose all context objects
    t_map::const_iterator iPos( m_map.begin() );
    t_map::const_iterator const iEnd( m_map.end() );
    for ( ; iPos != iEnd; ++iPos )
    {
        t_map::mapped_type pEntry = iPos->second;
 
        // service manager disposed separately
        if (!m_xSMgr.is() ||
            !iPos->first.startsWith( SMGR_SINGLETON ))
        {
            if (pEntry->lateInit)
            {
                // late init
                MutexGuard guard( m_mutex );
                if (pEntry->lateInit)
                {
                    pEntry->value.clear(); // release factory
                    pEntry->lateInit = false;
                    continue;
                }
            }
 
            Reference< lang::XComponent > xComp;
            pEntry->value >>= xComp;
            if (xComp.is())
            {
                if ( iPos->first == TDMGR_SINGLETON )
                {
                    xTDMgr = xComp;
                }
                else if ( iPos->first == AC_SINGLETON )
                {
                    xAC = xComp;
                }
                else // dispose immediately
                {
                    xComp->dispose();
                }
            }
        }
    }
 
    // dispose service manager
    try_dispose( m_xSMgr );
    m_xSMgr.clear();
    // dispose ac
    try_dispose( xAC );
    // dispose tdmgr; revokes callback from cppu runtime
    try_dispose( xTDMgr );
 
    iPos = m_map.begin();
    for ( ; iPos != iEnd; ++iPos )
        delete iPos->second;
    m_map.clear();
 
    // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI
    // proxies get finalized with arbitrary delay, so the bridge typically does
    // not dispose itself early enough before the process exits):
    uno_Environment ** envs;
    sal_Int32 envCount;
    uno_getRegisteredEnvironments(
        &envs, &envCount, &rtl_allocateMemory, OUString("java").pData);
    assert(envCount >= 0);
    assert(envCount == 0 || envs != nullptr);
    if (envs) {
        for (sal_Int32 i = 0; i != envCount; ++i) {
            assert(envs[i] != nullptr);
            assert(envs[i]->dispose != nullptr);
            (*envs[i]->dispose)(envs[i]);
        }
        std::free(envs);
    }
}
 
ComponentContext::ComponentContext(
    ContextEntry_Init const * pEntries, sal_Int32 nEntries,
    Reference< XComponentContext > const & xDelegate )
    : WeakComponentImplHelper( m_mutex ),
      m_xDelegate( xDelegate )
{
    for ( sal_Int32 nPos = 0; nPos < nEntries; ++nPos )
    {
        ContextEntry_Init const & rEntry = pEntries[ nPos ];
 
        if ( rEntry.name == SMGR_SINGLETON )
        {
            rEntry.value >>= m_xSMgr;
        }
 
        if (rEntry.bLateInitService)
        {
            // singleton entry
            m_map[ rEntry.name ] = new ContextEntry( Any(), true );
            // service
            m_map[ rEntry.name + "/service" ] = new ContextEntry( rEntry.value, false );
            // initial-arguments are provided as optional context entry
        }
        else
        {
            // only value, no late init factory nor string
            m_map[ rEntry.name ] = new ContextEntry( rEntry.value, false );
        }
    }
 
    if (!m_xSMgr.is() && m_xDelegate.is())
    {
        // wrap delegate's smgr XPropertySet into new smgr
        Reference< lang::XMultiComponentFactory > xMgr( m_xDelegate->getServiceManager() );
        if (xMgr.is())
        {
            osl_atomic_increment( &m_refCount );
            try
            {
                // create new smgr based on delegate's one
                m_xSMgr.set(
                    xMgr->createInstanceWithContext(
                        "com.sun.star.comp.stoc.OServiceManagerWrapper", xDelegate ),
                    UNO_QUERY );
                // patch DefaultContext property of new one
                Reference< beans::XPropertySet > xProps( m_xSMgr, UNO_QUERY );
                OSL_ASSERT( xProps.is() );
                if (xProps.is())
                {
                    Reference< XComponentContext > xThis( this );
                    xProps->setPropertyValue( "DefaultContext", Any( xThis ) );
                }
            }
            catch (...)
            {
                osl_atomic_decrement( &m_refCount );
                throw;
            }
            osl_atomic_decrement( &m_refCount );
            OSL_ASSERT( m_xSMgr.is() );
        }
    }
}
 
 
extern "C" { static void s_createComponentContext_v(va_list * pParam)
{
    ContextEntry_Init const  * pEntries     = va_arg(*pParam, ContextEntry_Init const *);
    sal_Int32                  nEntries     = va_arg(*pParam, sal_Int32);
    XComponentContext        * pDelegatee   = va_arg(*pParam, XComponentContext *);
    void                    ** ppContext    = va_arg(*pParam, void **);
    uno::Mapping             * pTarget2curr = va_arg(*pParam, uno::Mapping *);
 
    Reference<XComponentContext> xDelegate(pDelegatee, SAL_NO_ACQUIRE);
    Reference<XComponentContext> xContext;
 
    if (nEntries > 0)
    {
        try
        {
            ComponentContext * p = new ComponentContext( pEntries, nEntries, xDelegate );
            xContext.set(p);
            // listen delegate for disposing, to dispose this (wrapping) context first.
            DisposingForwarder::listen( Reference< lang::XComponent >::query( xDelegate ), p );
        }
        catch (Exception & exc)
        {
            SAL_WARN( "cppuhelper", exc );
            xContext.clear();
        }
    }
    else
    {
        xContext = xDelegate;
    }
 
    *ppContext = pTarget2curr->mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get());
}}
 
Reference< XComponentContext > SAL_CALL createComponentContext(
    ContextEntry_Init const * pEntries, sal_Int32 nEntries,
    Reference< XComponentContext > const & xDelegate )
{
    uno::Environment curr_env(Environment::getCurrent());
    uno::Environment source_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME);
 
    uno::Mapping curr2source(curr_env, source_env);
    uno::Mapping source2curr(source_env, curr_env);
 
    std::unique_ptr<ContextEntry_Init[]> mapped_entries(new ContextEntry_Init[nEntries]);
    for (sal_Int32 nPos = 0; nPos < nEntries; ++ nPos)
    {
        mapped_entries[nPos].bLateInitService = pEntries[nPos].bLateInitService;
        mapped_entries[nPos].name             = pEntries[nPos].name;
 
        uno_type_any_constructAndConvert(&mapped_entries[nPos].value,
                                         const_cast<void *>(pEntries[nPos].value.getValue()),
                                         pEntries[nPos].value.getValueTypeRef(),
                                         curr2source.get());
    }
 
    void * mapped_delegate = curr2source.mapInterface(xDelegate.get(), cppu::UnoType<decltype(xDelegate)>::get());
    XComponentContext * pXComponentContext = nullptr;
    source_env.invoke(s_createComponentContext_v, mapped_entries.get(), nEntries, mapped_delegate, &pXComponentContext, &source2curr);
    mapped_entries.reset();
 
    return Reference<XComponentContext>(pXComponentContext, SAL_NO_ACQUIRE);
}
 
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1019 Compound assignment expression 'args_ >>= args' is used inside condition.