<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: odbcconfig.cxx,v $
 * $Revision: 1.25 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"

#ifndef _DBAUI_ODBC_CONFIG_HXX_
#include "odbcconfig.hxx"
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _RTL_USTRBUF_HXX_
#include <rtl/ustrbuf.hxx>
#endif
#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif
#ifndef _OSL_PROCESS_H_
#include <osl/process.h>
#endif
#ifndef _THREAD_HXX_
#include <osl/thread.hxx>
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

#ifdef HAVE_ODBC_SUPPORT

#if defined(WIN) || defined(OS2)
#define ODBC_LIBRARY    "ODBC.DLL"
#define ODBC_UI_LIBRARY "ODBCINST.DLL"
#endif
#if defined WNT
#define ODBC_LIBRARY    "ODBC32.DLL"
#define ODBC_UI_LIBRARY "ODBCCP32.DLL"
#endif
#ifdef UNX
#ifdef MACOSX
#define ODBC_LIBRARY        "libiodbc.dylib"
#define ODBC_UI_LIBRARY     "libiodbcinst.dylib"
#else
#define ODBC_LIBRARY_1      "libodbc.so.1"
#define ODBC_UI_LIBRARY_1   "libodbcinst.so.1"
#define ODBC_LIBRARY        "libodbc.so"
#define ODBC_UI_LIBRARY     "libodbcinst.so"
#endif
#endif

// just to go with calling convention of windows
// so don't touch this
#if defined(WIN) || defined(WNT)
#define SQL_API __stdcall
// At least under some circumstances, the below #include <odbc/sqlext.h> re-
// defines SQL_API to an empty string, leading to a compiler warning on MSC; to
// not break the current behavior, this is worked around by locally disabling
// that warning:
#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4005)
#endif
#endif // defined(WIN) || defined(WNT)

#if defined(OS2)
#define ALLREADY_HAVE_OS2_TYPES
#define DONT_TD_VOID
#endif

#ifdef SYSTEM_ODBC_HEADERS
#include <sqlext.h>
#else
#ifndef __SQLEXT_H
#include <odbc/sqlext.h>
#endif
#endif

#if defined(WIN) || defined(WNT)
#if defined _MSC_VER
#pragma warning(pop)
#endif
#undef SQL_API
#define SQL_API __stdcall
#endif // defined(WIN) || defined(WNT)
// from here on you can do what you want to

#if defined(OS2)
#define SQL_API _System
#endif // defined(OS2)

#else

#define ODBC_LIBRARY    ""
#define ODBC_UI_LIBRARY ""

#endif  // HAVE_ODBC_SUPPORT

//.........................................................................
namespace dbaui
{
//.........................................................................

#ifdef HAVE_ODBC_SUPPORT
typedef SQLRETURN (SQL_API* TSQLManageDataSource) (SQLHWND hwndParent);
typedef SQLRETURN (SQL_API* TSQLAllocHandle) (SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE* OutputHandlePtr);
typedef SQLRETURN (SQL_API* TSQLFreeHandle) (SQLSMALLINT HandleType, SQLHANDLE Handle);
typedef SQLRETURN (SQL_API* TSQLSetEnvAttr) (SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
typedef SQLRETURN (SQL_API* TSQLDataSources) (SQLHENV EnvironmentHandle, SQLUSMALLINT   Direction, SQLCHAR* ServerName,
                                SQLSMALLINT BufferLength1, SQLSMALLINT* NameLength1Ptr, SQLCHAR* Description, SQLSMALLINT BufferLength2, SQLSMALLINT* NameLength2Ptr);

#define NSQLManageDataSource(a) (*(TSQLManageDataSource)(m_pSQLManageDataSource))(a)
#define NSQLAllocHandle(a,b,c) (*(TSQLAllocHandle)(m_pAllocHandle))(a,b,c)
#define NSQLFreeHandle(a,b) (*(TSQLFreeHandle)(m_pFreeHandle))(a,b)
#define NSQLSetEnvAttr(a,b,c,d) (*(TSQLSetEnvAttr)(m_pSetEnvAttr))(a,b,c,d)
#define NSQLDataSources(a,b,c,d,e,f,g,h) (*(TSQLDataSources)(m_pDataSources))(a,b,c,d,e,f,g,h)
#endif

//=========================================================================
//= OOdbcLibWrapper
//=========================================================================
DBG_NAME(OOdbcLibWrapper)
//-------------------------------------------------------------------------
#ifdef HAVE_ODBC_SUPPORT
OOdbcLibWrapper::OOdbcLibWrapper()
    :m_pOdbcLib(NULL)
{
    DBG_CTOR(OOdbcLibWrapper,NULL);

}
#endif

//-------------------------------------------------------------------------
sal_Bool OOdbcLibWrapper::load(const sal_Char* _pLibPath)
{
    m_sLibPath = ::rtl::OUString::createFromAscii(_pLibPath);
#ifdef HAVE_ODBC_SUPPORT
    // load the module
    m_pOdbcLib = osl_loadModule(m_sLibPath.pData, SAL_LOADMODULE_NOW);
    return (NULL != m_pOdbcLib);
#endif
}

//-------------------------------------------------------------------------
void OOdbcLibWrapper::unload()
{
#ifdef HAVE_ODBC_SUPPORT
    if (isLoaded())
    {
        osl_unloadModule(m_pOdbcLib);
        m_pOdbcLib = NULL;
    }
#endif
}

//-------------------------------------------------------------------------
oslGenericFunction OOdbcLibWrapper::loadSymbol(const sal_Char* _pFunctionName)
{
    return osl_getFunctionSymbol(m_pOdbcLib, ::rtl::OUString::createFromAscii(_pFunctionName).pData);
}

//-------------------------------------------------------------------------
OOdbcLibWrapper::~OOdbcLibWrapper()
{
    unload();

    DBG_DTOR(OOdbcLibWrapper,NULL);
}

//=========================================================================
//= OOdbcEnumeration
//=========================================================================
struct OdbcTypesImpl
{
#ifdef HAVE_ODBC_SUPPORT
    SQLHANDLE   hEnvironment;
    OdbcTypesImpl() : hEnvironment(0) { }
#else
    void*       pDummy;
#endif
};
DBG_NAME(OOdbcEnumeration)
//-------------------------------------------------------------------------
OOdbcEnumeration::OOdbcEnumeration()
#ifdef HAVE_ODBC_SUPPORT
    :m_pAllocHandle(NULL)
    ,m_pSetEnvAttr(NULL)
    ,m_pDataSources(NULL)
    ,m_pImpl(new OdbcTypesImpl)
#endif
{
    DBG_CTOR(OOdbcEnumeration,NULL);

    sal_Bool bLoaded = load(ODBC_LIBRARY);
#ifdef ODBC_LIBRARY_1
    if ( !bLoaded )
        bLoaded = load(ODBC_LIBRARY_1);
#endif

    if ( bLoaded )
    {
#ifdef HAVE_ODBC_SUPPORT
        // load the generic functions
        m_pAllocHandle = loadSymbol("SQLAllocHandle");
        m_pFreeHandle = loadSymbol("SQLFreeHandle");
        m_pSetEnvAttr = loadSymbol("SQLSetEnvAttr");
        m_pDataSources = loadSymbol("SQLDataSources");

        // all or nothing
        if (!m_pAllocHandle || !m_pSetEnvAttr || !m_pDataSources || !m_pFreeHandle)
        {
            unload();
            m_pAllocHandle = m_pFreeHandle = m_pSetEnvAttr = m_pDataSources = NULL;
        }
#endif
    }
}

//-------------------------------------------------------------------------
OOdbcEnumeration::~OOdbcEnumeration()
{
    freeEnv();
    delete m_pImpl;

    DBG_DTOR(OOdbcEnumeration,NULL);
}

//-------------------------------------------------------------------------
sal_Bool OOdbcEnumeration::allocEnv()
{
    OSL_ENSURE(isLoaded(), "OOdbcEnumeration::allocEnv: not loaded!");
    if (!isLoaded())
        return sal_False;

#ifdef HAVE_ODBC_SUPPORT
    if (m_pImpl->hEnvironment)
        // nothing to do
        return sal_True;
    SQLRETURN nResult = NSQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_pImpl->hEnvironment);
    if (SQL_SUCCESS != nResult)
        // can't do anything without environment
        return sal_False;

    NSQLSetEnvAttr(m_pImpl->hEnvironment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
    return sal_True;
#else
    return sal_False;
#endif
}

//-------------------------------------------------------------------------
void OOdbcEnumeration::freeEnv()
{
#ifdef HAVE_ODBC_SUPPORT
    if (m_pImpl->hEnvironment)
        NSQLFreeHandle(SQL_HANDLE_ENV, m_pImpl->hEnvironment);
    m_pImpl->hEnvironment  = 0;
#endif
}

//-------------------------------------------------------------------------
void OOdbcEnumeration::getDatasourceNames(StringBag& _rNames)
{
    OSL_ENSURE(isLoaded(), "OOdbcEnumeration::getDatasourceNames: not loaded!");
    if (!isLoaded())
        return;

    if (!allocEnv())
    {
        OSL_ENSURE(sal_False, "OOdbcEnumeration::getDatasourceNames: could not allocate an ODBC environment!");
        return;
    }

#ifdef HAVE_ODBC_SUPPORT
    // now that we have an environment collect the data source names
    UCHAR szDSN[SQL_MAX_DSN_LENGTH+1];
    SWORD pcbDSN;
    UCHAR szDescription[1024+1];
    SWORD pcbDescription;
    SQLRETURN nResult = SQL_SUCCESS;
    rtl_TextEncoding nTextEncoding = osl_getThreadTextEncoding();

    for (   nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_FIRST, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription);
            ;
            nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_NEXT, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription)
        )
    {
        if (nResult != SQL_SUCCESS)
            // no further error handling
            break;
        else
        {
            ::rtl::OUString aCurrentDsn(reinterpret_cast<const char*>(szDSN),pcbDSN, nTextEncoding);
            _rNames.insert(aCurrentDsn);
        }
    }
#endif
}

#ifdef HAVE_ODBC_ADMINISTRATION

//=========================================================================
//= ProcessTerminationWait
//=========================================================================
class ProcessTerminationWait : public ::osl::Thread
{
    oslProcess  m_hProcessHandle;
    Link        m_aFinishHdl;

public:
    ProcessTerminationWait( oslProcess _hProcessHandle, const Link& _rFinishHdl )
        :m_hProcessHandle( _hProcessHandle )
        ,m_aFinishHdl( _rFinishHdl )
    {
    }

protected:
    virtual void SAL_CALL run()
    {
        osl_joinProcess( m_hProcessHandle );
        Application::PostUserEvent( m_aFinishHdl );
    }
};

//=========================================================================
//= OOdbcManagement
//=========================================================================
//-------------------------------------------------------------------------
OOdbcManagement::OOdbcManagement( const Link& _rAsyncFinishCallback )
    :m_pProcessWait( NULL )
    ,m_aAsyncFinishCallback( _rAsyncFinishCallback )
{
}

//-------------------------------------------------------------------------
OOdbcManagement::~OOdbcManagement()
{
    // wait for our thread to be finished
    if ( m_pProcessWait.get() )
        m_pProcessWait->join();
}

//-------------------------------------------------------------------------
bool OOdbcManagement::manageDataSources_async()
{
    OSL_PRECOND( !isRunning(), "OOdbcManagement::manageDataSources_async: still running from the previous call!" );
    if ( isRunning() )
        return false;

    // this is done in an external process, due to #i78733#
    // (and note this whole functionality is supported on Windows only, ATM)
    ::rtl::OUString sExecutableName( RTL_CONSTASCII_USTRINGPARAM( "odbcconfig.exe" ) );
    oslProcess hProcessHandle(0);
    oslProcessError eError = osl_executeProcess( sExecutableName.pData, NULL, 0, 0, NULL, NULL, NULL, 0, &hProcessHandle );
    if ( eError != osl_Process_E_None )
        return false;

    m_pProcessWait.reset( new ProcessTerminationWait( hProcessHandle, m_aAsyncFinishCallback ) );
    m_pProcessWait->create();
    return true;
}

//-------------------------------------------------------------------------
bool OOdbcManagement::isRunning() const
{
    return ( m_pProcessWait.get() && m_pProcessWait->isRunning() );
}

#endif // HAVE_ODBC_ADMINISTRATION

//.........................................................................
}   // namespace dbaui
//.........................................................................
<commit_msg>INTEGRATION: CWS cmcfixes44 (1.25.18); FILE MERGED 2008/04/24 08:27:36 cmc 1.25.18.1: #i88676# osl_freeProcessHandle is missing after a successful call to osl_executeProcess<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: odbcconfig.cxx,v $
 * $Revision: 1.26 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"

#ifndef _DBAUI_ODBC_CONFIG_HXX_
#include "odbcconfig.hxx"
#endif
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _RTL_USTRBUF_HXX_
#include <rtl/ustrbuf.hxx>
#endif
#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif
#ifndef _OSL_PROCESS_H_
#include <osl/process.h>
#endif
#ifndef _THREAD_HXX_
#include <osl/thread.hxx>
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

#ifdef HAVE_ODBC_SUPPORT

#if defined(WIN) || defined(OS2)
#define ODBC_LIBRARY    "ODBC.DLL"
#define ODBC_UI_LIBRARY "ODBCINST.DLL"
#endif
#if defined WNT
#define ODBC_LIBRARY    "ODBC32.DLL"
#define ODBC_UI_LIBRARY "ODBCCP32.DLL"
#endif
#ifdef UNX
#ifdef MACOSX
#define ODBC_LIBRARY        "libiodbc.dylib"
#define ODBC_UI_LIBRARY     "libiodbcinst.dylib"
#else
#define ODBC_LIBRARY_1      "libodbc.so.1"
#define ODBC_UI_LIBRARY_1   "libodbcinst.so.1"
#define ODBC_LIBRARY        "libodbc.so"
#define ODBC_UI_LIBRARY     "libodbcinst.so"
#endif
#endif

// just to go with calling convention of windows
// so don't touch this
#if defined(WIN) || defined(WNT)
#define SQL_API __stdcall
// At least under some circumstances, the below #include <odbc/sqlext.h> re-
// defines SQL_API to an empty string, leading to a compiler warning on MSC; to
// not break the current behavior, this is worked around by locally disabling
// that warning:
#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4005)
#endif
#endif // defined(WIN) || defined(WNT)

#if defined(OS2)
#define ALLREADY_HAVE_OS2_TYPES
#define DONT_TD_VOID
#endif

#ifdef SYSTEM_ODBC_HEADERS
#include <sqlext.h>
#else
#ifndef __SQLEXT_H
#include <odbc/sqlext.h>
#endif
#endif

#if defined(WIN) || defined(WNT)
#if defined _MSC_VER
#pragma warning(pop)
#endif
#undef SQL_API
#define SQL_API __stdcall
#endif // defined(WIN) || defined(WNT)
// from here on you can do what you want to

#if defined(OS2)
#define SQL_API _System
#endif // defined(OS2)

#else

#define ODBC_LIBRARY    ""
#define ODBC_UI_LIBRARY ""

#endif  // HAVE_ODBC_SUPPORT

//.........................................................................
namespace dbaui
{
//.........................................................................

#ifdef HAVE_ODBC_SUPPORT
typedef SQLRETURN (SQL_API* TSQLManageDataSource) (SQLHWND hwndParent);
typedef SQLRETURN (SQL_API* TSQLAllocHandle) (SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE* OutputHandlePtr);
typedef SQLRETURN (SQL_API* TSQLFreeHandle) (SQLSMALLINT HandleType, SQLHANDLE Handle);
typedef SQLRETURN (SQL_API* TSQLSetEnvAttr) (SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
typedef SQLRETURN (SQL_API* TSQLDataSources) (SQLHENV EnvironmentHandle, SQLUSMALLINT   Direction, SQLCHAR* ServerName,
                                SQLSMALLINT BufferLength1, SQLSMALLINT* NameLength1Ptr, SQLCHAR* Description, SQLSMALLINT BufferLength2, SQLSMALLINT* NameLength2Ptr);

#define NSQLManageDataSource(a) (*(TSQLManageDataSource)(m_pSQLManageDataSource))(a)
#define NSQLAllocHandle(a,b,c) (*(TSQLAllocHandle)(m_pAllocHandle))(a,b,c)
#define NSQLFreeHandle(a,b) (*(TSQLFreeHandle)(m_pFreeHandle))(a,b)
#define NSQLSetEnvAttr(a,b,c,d) (*(TSQLSetEnvAttr)(m_pSetEnvAttr))(a,b,c,d)
#define NSQLDataSources(a,b,c,d,e,f,g,h) (*(TSQLDataSources)(m_pDataSources))(a,b,c,d,e,f,g,h)
#endif

//=========================================================================
//= OOdbcLibWrapper
//=========================================================================
DBG_NAME(OOdbcLibWrapper)
//-------------------------------------------------------------------------
#ifdef HAVE_ODBC_SUPPORT
OOdbcLibWrapper::OOdbcLibWrapper()
    :m_pOdbcLib(NULL)
{
    DBG_CTOR(OOdbcLibWrapper,NULL);

}
#endif

//-------------------------------------------------------------------------
sal_Bool OOdbcLibWrapper::load(const sal_Char* _pLibPath)
{
    m_sLibPath = ::rtl::OUString::createFromAscii(_pLibPath);
#ifdef HAVE_ODBC_SUPPORT
    // load the module
    m_pOdbcLib = osl_loadModule(m_sLibPath.pData, SAL_LOADMODULE_NOW);
    return (NULL != m_pOdbcLib);
#endif
}

//-------------------------------------------------------------------------
void OOdbcLibWrapper::unload()
{
#ifdef HAVE_ODBC_SUPPORT
    if (isLoaded())
    {
        osl_unloadModule(m_pOdbcLib);
        m_pOdbcLib = NULL;
    }
#endif
}

//-------------------------------------------------------------------------
oslGenericFunction OOdbcLibWrapper::loadSymbol(const sal_Char* _pFunctionName)
{
    return osl_getFunctionSymbol(m_pOdbcLib, ::rtl::OUString::createFromAscii(_pFunctionName).pData);
}

//-------------------------------------------------------------------------
OOdbcLibWrapper::~OOdbcLibWrapper()
{
    unload();

    DBG_DTOR(OOdbcLibWrapper,NULL);
}

//=========================================================================
//= OOdbcEnumeration
//=========================================================================
struct OdbcTypesImpl
{
#ifdef HAVE_ODBC_SUPPORT
    SQLHANDLE   hEnvironment;
    OdbcTypesImpl() : hEnvironment(0) { }
#else
    void*       pDummy;
#endif
};
DBG_NAME(OOdbcEnumeration)
//-------------------------------------------------------------------------
OOdbcEnumeration::OOdbcEnumeration()
#ifdef HAVE_ODBC_SUPPORT
    :m_pAllocHandle(NULL)
    ,m_pSetEnvAttr(NULL)
    ,m_pDataSources(NULL)
    ,m_pImpl(new OdbcTypesImpl)
#endif
{
    DBG_CTOR(OOdbcEnumeration,NULL);

    sal_Bool bLoaded = load(ODBC_LIBRARY);
#ifdef ODBC_LIBRARY_1
    if ( !bLoaded )
        bLoaded = load(ODBC_LIBRARY_1);
#endif

    if ( bLoaded )
    {
#ifdef HAVE_ODBC_SUPPORT
        // load the generic functions
        m_pAllocHandle = loadSymbol("SQLAllocHandle");
        m_pFreeHandle = loadSymbol("SQLFreeHandle");
        m_pSetEnvAttr = loadSymbol("SQLSetEnvAttr");
        m_pDataSources = loadSymbol("SQLDataSources");

        // all or nothing
        if (!m_pAllocHandle || !m_pSetEnvAttr || !m_pDataSources || !m_pFreeHandle)
        {
            unload();
            m_pAllocHandle = m_pFreeHandle = m_pSetEnvAttr = m_pDataSources = NULL;
        }
#endif
    }
}

//-------------------------------------------------------------------------
OOdbcEnumeration::~OOdbcEnumeration()
{
    freeEnv();
    delete m_pImpl;

    DBG_DTOR(OOdbcEnumeration,NULL);
}

//-------------------------------------------------------------------------
sal_Bool OOdbcEnumeration::allocEnv()
{
    OSL_ENSURE(isLoaded(), "OOdbcEnumeration::allocEnv: not loaded!");
    if (!isLoaded())
        return sal_False;

#ifdef HAVE_ODBC_SUPPORT
    if (m_pImpl->hEnvironment)
        // nothing to do
        return sal_True;
    SQLRETURN nResult = NSQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_pImpl->hEnvironment);
    if (SQL_SUCCESS != nResult)
        // can't do anything without environment
        return sal_False;

    NSQLSetEnvAttr(m_pImpl->hEnvironment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
    return sal_True;
#else
    return sal_False;
#endif
}

//-------------------------------------------------------------------------
void OOdbcEnumeration::freeEnv()
{
#ifdef HAVE_ODBC_SUPPORT
    if (m_pImpl->hEnvironment)
        NSQLFreeHandle(SQL_HANDLE_ENV, m_pImpl->hEnvironment);
    m_pImpl->hEnvironment  = 0;
#endif
}

//-------------------------------------------------------------------------
void OOdbcEnumeration::getDatasourceNames(StringBag& _rNames)
{
    OSL_ENSURE(isLoaded(), "OOdbcEnumeration::getDatasourceNames: not loaded!");
    if (!isLoaded())
        return;

    if (!allocEnv())
    {
        OSL_ENSURE(sal_False, "OOdbcEnumeration::getDatasourceNames: could not allocate an ODBC environment!");
        return;
    }

#ifdef HAVE_ODBC_SUPPORT
    // now that we have an environment collect the data source names
    UCHAR szDSN[SQL_MAX_DSN_LENGTH+1];
    SWORD pcbDSN;
    UCHAR szDescription[1024+1];
    SWORD pcbDescription;
    SQLRETURN nResult = SQL_SUCCESS;
    rtl_TextEncoding nTextEncoding = osl_getThreadTextEncoding();

    for (   nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_FIRST, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription);
            ;
            nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_NEXT, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription)
        )
    {
        if (nResult != SQL_SUCCESS)
            // no further error handling
            break;
        else
        {
            ::rtl::OUString aCurrentDsn(reinterpret_cast<const char*>(szDSN),pcbDSN, nTextEncoding);
            _rNames.insert(aCurrentDsn);
        }
    }
#endif
}

#ifdef HAVE_ODBC_ADMINISTRATION

//=========================================================================
//= ProcessTerminationWait
//=========================================================================
class ProcessTerminationWait : public ::osl::Thread
{
    oslProcess  m_hProcessHandle;
    Link        m_aFinishHdl;

public:
    ProcessTerminationWait( oslProcess _hProcessHandle, const Link& _rFinishHdl )
        :m_hProcessHandle( _hProcessHandle )
        ,m_aFinishHdl( _rFinishHdl )
    {
    }

protected:
    virtual void SAL_CALL run()
    {
        osl_joinProcess( m_hProcessHandle );
        osl_freeProcessHandle( m_hProcessHandle );
        Application::PostUserEvent( m_aFinishHdl );
    }
};

//=========================================================================
//= OOdbcManagement
//=========================================================================
//-------------------------------------------------------------------------
OOdbcManagement::OOdbcManagement( const Link& _rAsyncFinishCallback )
    :m_pProcessWait( NULL )
    ,m_aAsyncFinishCallback( _rAsyncFinishCallback )
{
}

//-------------------------------------------------------------------------
OOdbcManagement::~OOdbcManagement()
{
    // wait for our thread to be finished
    if ( m_pProcessWait.get() )
        m_pProcessWait->join();
}

//-------------------------------------------------------------------------
bool OOdbcManagement::manageDataSources_async()
{
    OSL_PRECOND( !isRunning(), "OOdbcManagement::manageDataSources_async: still running from the previous call!" );
    if ( isRunning() )
        return false;

    // this is done in an external process, due to #i78733#
    // (and note this whole functionality is supported on Windows only, ATM)
    ::rtl::OUString sExecutableName( RTL_CONSTASCII_USTRINGPARAM( "odbcconfig.exe" ) );
    oslProcess hProcessHandle(0);
    oslProcessError eError = osl_executeProcess( sExecutableName.pData, NULL, 0, 0, NULL, NULL, NULL, 0, &hProcessHandle );
    if ( eError != osl_Process_E_None )
        return false;

    m_pProcessWait.reset( new ProcessTerminationWait( hProcessHandle, m_aAsyncFinishCallback ) );
    m_pProcessWait->create();
    return true;
}

//-------------------------------------------------------------------------
bool OOdbcManagement::isRunning() const
{
    return ( m_pProcessWait.get() && m_pProcessWait->isRunning() );
}

#endif // HAVE_ODBC_ADMINISTRATION

//.........................................................................
}   // namespace dbaui
//.........................................................................
<|endoftext|>


<commit_before>/*
qgvdial is a cross platform Google Voice Dialer
Copyright (C) 2009-2013  Yuvraaj Kelkar

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Contact: yuvraaj@gmail.com
*/

#include "MainWindow.h"
#include "Lib.h"

#include <iostream>
using namespace std;

QtMsgHandler    pOldHandler = NULL;
MainWindow     *win = NULL;

QFile fLogfile;       //! Logfile
int   logLevel = 5;   //! Log level
int   logCounter = 0; //! Number of log entries since the last log flush
#define LOG_FLUSH_LEVEL 0

QStringList g_arrLogFiles;

const char *ignoreMsgs[] = {
    "MPanRecognizerTouch",
    "QGLWindowSurface",
    "brokenTexSubImage",
    "FullClearOnEveryFrame",
    "hijackWindow"
};

void
qgv_LogFlush()
{
    if (logCounter) {
        logCounter = 0;
        fLogfile.flush ();
        cout.flush();
    }
}

void
myMessageOutput(QtMsgType type, const char *msg)
{
    int level = -1;
    switch (type) {
    case QtDebugMsg:
        level = 3;
        break;
    case QtWarningMsg:
        level = 2;
        break;
    case QtCriticalMsg:
        level = 1;
        break;
    case QtFatalMsg:
        level = 0;
        break;
    }

    QDateTime dt = QDateTime::currentDateTime ();
    QString strLog = QString("%1 : %2 : %3")
                     .arg(dt.toString ("yyyy-MM-dd hh:mm:ss.zzz"))
                     .arg(level)
                     .arg(msg);

    // Ignore some log messages.
    for (quint16 i = 0; i < COUNT_OF(ignoreMsgs); i++) {
        if (strLog.contains(ignoreMsgs[i])) {
            return;
        }
    }

    // Send to standard output.
    // I'm not using endl here because endl causes flushes
    cout << strLog.toLatin1().constData() << "\n";

    if ((level <= logLevel) && (NULL != win)) {
        win->log(dt, level, strLog);
    }

    strLog += '\n';
    if (level <= logLevel) {
        if (fLogfile.isOpen ()) {
            // Append it to the file
            fLogfile.write(strLog.toLatin1 ());

            ++logCounter;
            if (logCounter > LOG_FLUSH_LEVEL) {
                qgv_LogFlush ();
            }
        }
    }

    if (QtFatalMsg == type) {
        abort();
    }
}//myMessageOutput

static void
initLogging ()
{
    Lib &lib = Lib::ref ();
    QString strLogfile = lib.getLogsDir ();
    strLogfile += QDir::separator ();
    strLogfile += "qgvdial-startup.log";

    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::ReadWrite);

    pOldHandler = qInstallMsgHandler(myMessageOutput);
}//initLogging

static void
initLogRotate()
{
    Lib &lib = Lib::ref ();
    QString strLogfile = lib.getLogsDir ();
    strLogfile += QDir::separator ();
    strLogfile += "qgvdial.log";

    for (int i = 4; i >= 0; i--) {
        g_arrLogFiles.append (QString("%1.%2").arg(strLogfile).arg(i));
    }
    g_arrLogFiles.append (strLogfile);

    QFile::remove (g_arrLogFiles[0]);
    for (int i = 1; i < g_arrLogFiles.count (); i++) {
        if (QFile::exists (g_arrLogFiles[i])) {
            QFile::rename (g_arrLogFiles[i], g_arrLogFiles[i-1]);
        }
    }

    fLogfile.close ();
    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::ReadWrite);
}//initLogRotate

static void
deinitLogging ()
{
    win = NULL;
    fLogfile.close ();
}//deinitLogging

Q_DECL_EXPORT int
main(int argc, char *argv[])
{
    QCoreApplication *app = createAppObject(argc, argv);
    if (NULL == app) {
        // For whatever reason (not necessarily a lack of memory), the fn has
        // told us that creating this app is NOT possible. GTFO NOW!
        return (-1);
    }

    // Phonon needs this. Doesn't hurt even if we don't use Phonon.
    app->setApplicationName ("qgvdial");

    initLogging ();
    initLogRotate ();

    win = new MainWindow(app);
    win->init();

    int rv = app->exec();

    // Safety...
    MainWindow *temp = win;
    deinitLogging ();

    delete temp;
    delete app;
    return rv;
}
<commit_msg>message handler<commit_after>/*
qgvdial is a cross platform Google Voice Dialer
Copyright (C) 2009-2013  Yuvraaj Kelkar

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Contact: yuvraaj@gmail.com
*/

#include "MainWindow.h"
#include "Lib.h"

#include <iostream>
using namespace std;

#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
QtMessageHandler pOldHandler = NULL;
#else
QtMsgHandler     pOldHandler = NULL;
#endif

MainWindow     *win = NULL;

QFile fLogfile;       //! Logfile
int   logLevel = 5;   //! Log level
int   logCounter = 0; //! Number of log entries since the last log flush
#define LOG_FLUSH_LEVEL 0

QStringList g_arrLogFiles;

const char *ignoreMsgs[] = {
    "MPanRecognizerTouch",
    "QGLWindowSurface",
    "brokenTexSubImage",
    "FullClearOnEveryFrame",
    "hijackWindow"
};

void
qgv_LogFlush()
{
    if (logCounter) {
        logCounter = 0;
        fLogfile.flush ();
        cout.flush();
    }
}

void
myMessageOutput(QtMsgType type, const char *msg)
{
    int level = -1;
    switch (type) {
    case QtDebugMsg:
        level = 3;
        break;
    case QtWarningMsg:
        level = 2;
        break;
    case QtCriticalMsg:
        level = 1;
        break;
    case QtFatalMsg:
        level = 0;
        break;
    }

    QDateTime dt = QDateTime::currentDateTime ();
    QString strLog = QString("%1 : %2 : %3")
                     .arg(dt.toString ("yyyy-MM-dd hh:mm:ss.zzz"))
                     .arg(level)
                     .arg(msg);

    // Ignore some log messages.
    for (quint16 i = 0; i < COUNT_OF(ignoreMsgs); i++) {
        if (strLog.contains(ignoreMsgs[i])) {
            return;
        }
    }

    // Send to standard output.
    // I'm not using endl here because endl causes flushes
    cout << strLog.toLatin1().constData() << "\n";

    if ((level <= logLevel) && (NULL != win)) {
        win->log(dt, level, strLog);
    }

    strLog += '\n';
    if (level <= logLevel) {
        if (fLogfile.isOpen ()) {
            // Append it to the file
            fLogfile.write(strLog.toLatin1 ());

            ++logCounter;
            if (logCounter > LOG_FLUSH_LEVEL) {
                qgv_LogFlush ();
            }
        }
    }

    if (QtFatalMsg == type) {
        abort();
    }
}//myMessageOutput

#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
void
myQt5MessageOutput(QtMsgType type, const QMessageLogContext &ctx,
                   const QString &msg)
{
   QString msg1 = QString("%1(%2): %3")
                     .arg(ctx.function).arg(ctx.line).arg(msg);
   myMessageOutput(type, msg1.toLatin1().constData());
}//myQt5MessageOutput
#endif

static void
initLogging ()
{
    Lib &lib = Lib::ref ();
    QString strLogfile = lib.getLogsDir ();
    strLogfile += QDir::separator ();
    strLogfile += "qgvdial-startup.log";

    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::ReadWrite);

#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
    pOldHandler = qInstallMessageHandler(myQt5MessageOutput);
#else
    pOldHandler = qInstallMsgHandler(myMessageOutput);
#endif
}//initLogging

static void
initLogRotate()
{
    Lib &lib = Lib::ref ();
    QString strLogfile = lib.getLogsDir ();
    strLogfile += QDir::separator ();
    strLogfile += "qgvdial.log";

    for (int i = 4; i >= 0; i--) {
        g_arrLogFiles.append (QString("%1.%2").arg(strLogfile).arg(i));
    }
    g_arrLogFiles.append (strLogfile);

    QFile::remove (g_arrLogFiles[0]);
    for (int i = 1; i < g_arrLogFiles.count (); i++) {
        if (QFile::exists (g_arrLogFiles[i])) {
            QFile::rename (g_arrLogFiles[i], g_arrLogFiles[i-1]);
        }
    }

    fLogfile.close ();
    fLogfile.setFileName (strLogfile);
    fLogfile.open (QIODevice::ReadWrite);
}//initLogRotate

static void
deinitLogging ()
{
    win = NULL;
    fLogfile.close ();
}//deinitLogging

Q_DECL_EXPORT int
main(int argc, char *argv[])
{
    QCoreApplication *app = createAppObject(argc, argv);
    if (NULL == app) {
        // For whatever reason (not necessarily a lack of memory), the fn has
        // told us that creating this app is NOT possible. GTFO NOW!
        return (-1);
    }

    // Phonon needs this. Doesn't hurt even if we don't use Phonon.
    app->setApplicationName ("qgvdial");

    initLogging ();
    initLogRotate ();

    win = new MainWindow(app);
    win->init();

    int rv = app->exec();

    // Safety...
    MainWindow *temp = win;
    deinitLogging ();

    delete temp;
    delete app;
    return rv;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <sensor_msgs/LaserScan.h>
#include <opencv2/opencv.hpp>
#include <tf/tf.h>
#include <tf/transform_listener.h>

#include "endlineturnmp.h"

cv::Mat scannedData;
float factor = 80.0f; // 1mt = 40px
float imageSize = 800;
cv::Point2f center(400,400);
std::vector<cv::Vec4i> lines;

cv::Point2f target, targetDirection;

float pointLineDistance(const cv::Point2f &point, const cv::Vec4i line)
{
    cv::Point pt = point;

    // take 2 points on the line
    cv::Point2f begin(line.val[2],line.val[3]);
    cv::Point2f end(line.val[0],line.val[1]);

    // translate to the origin
    pt = point - begin;
    end = end - begin;

    double area = pt.cross(end);
    return area / cv::norm(end);
}

int nearestLineIndex(const cv::Point2f &point, const std::vector<cv::Vec4i> &linesVector)
{
    float minDistance = std::numeric_limits<float>::max();
    int nearestLineIndex = -1;

    int count = 0;
    for (cv::Vec4i line : linesVector)
    {
        // compute line-point distance
        float currentDistance = std::abs(pointLineDistance(point, line));

        std::cout << currentDistance << " - ";

        if (currentDistance < minDistance)
        {
            nearestLineIndex = count;
            minDistance = currentDistance;
        }

        count = count + 1;
    }

    std::cout << std::endl << minDistance << std::endl;

    return nearestLineIndex;
}

cv::Point2f getSnapPointToLine(const cv::Point2f &point, const cv::Vec4i line)
{
    float distance = pointLineDistance(point, line);

    cv::Point2f A(line[0],line[1]);

    cv::Vec2f A_pointVector(point - A);

    float A_pointDistance = cv::norm(A_pointVector);

    float A_snapDistance = std::sqrt(A_pointDistance*A_pointDistance - distance*distance);

    cv::Vec2f lineVector(line[2]-line[0], line[3]-line[1]);
    lineVector = A_snapDistance * cv::normalize(lineVector);

    return cv::Point2f(A.x + lineVector.val[0], A.y + lineVector.val[1]);
}

void morphClosure(const cv::Mat &src, cv::Mat &dst)
{
    int dilationType = cv::MORPH_RECT;
    int dilationSize = 3;
    cv::Mat element = getStructuringElement( dilationType,
                                         cv::Size( 2*dilationSize + 1, 2*dilationSize+1 ),
                                         cv::Point( dilationSize, dilationSize ) );
    cv::Mat tmp;
    cv::dilate(src, tmp, element);
    cv::erode(tmp, dst, element);
}

void scan_callback(const sensor_msgs::LaserScan::ConstPtr &msg)
{
    // get laser points
    scannedData = cv::Mat::zeros(cv::Size(800,800), CV_8UC1);

    float angle = msg->angle_min;
    for (int i = 0; i < msg->ranges.size(); i++)
    {
        float range = msg->ranges[i];
        if (range >= msg->range_min && range <= msg->range_max)
        {
            cv::Point2f pt(range * std::cos(angle), -1 * range * std::sin(angle));
            scannedData.at<uchar>(pt*factor + center) = 255;
        }

        angle = angle + msg->angle_increment;
    }

    // perform a closure (dilation+erosion)
    cv::Mat closedScannedData;
    morphClosure(scannedData, closedScannedData);

    // extract lines from laser points
    HoughLinesP( closedScannedData, lines, 2, CV_PI/90, 80, 25, 50 );

    // get nearest line index
    int idx = nearestLineIndex(target*factor+center, lines);
    float distance = pointLineDistance(target*factor+center, lines[idx]);

    std::cout << idx << " - (" << target.x << "," << target.y << ")" << " - " << lines[idx] << " - " << distance << std::endl;

    // plot lines, points...
    cv::Mat linesColor;
    cv::cvtColor(scannedData, linesColor, CV_GRAY2BGR);

    cv::circle(linesColor, cv::Point2f(0,0)*factor+center, 3, cv::Scalar(200,200,200),3);
    for( size_t i = 0; i < lines.size(); i++ )
    {
      cv::Vec4i l = lines[i];
      cv::line( linesColor,
                cv::Point2f(l[0], l[1]),
                cv::Point2f(l[2], l[3]),
                i==idx?cv::Scalar(0,255,0):cv::Scalar(0,0,255),
                1, CV_AA );
      cv::circle(linesColor, cv::Point2f(l[0], l[1]), 2, cv::Scalar(200,200,100));
      cv::circle(linesColor, cv::Point2f(l[2], l[3]), 2, cv::Scalar(100,200,200));
    }

    /// Move the target 1mt in front of the line
    /*// get an ortogonal vector
    cv::Vec2f lineVector((lines[idx])[0] - (lines[idx])[2],
                         (lines[idx])[1] - (lines[idx])[3]);
    /// TODO: check 0 values...
    cv::Vec2f ortoLineVector(-lineVector.val[1], lineVector.val[0]);
    ortoLineVector = ortoLineVector / cv::norm(ortoLineVector);*/

    cv::circle(linesColor, target*factor+center, 3, cv::Scalar(250,50,250),3);

    // snap target to the nearest line
    target = getSnapPointToLine(target*factor+center, lines[idx]);

    cv::circle(linesColor, target, 3, cv::Scalar(255,0,0),3);

    cv::imshow("lines", linesColor);
    cv::waitKey(33);
}

int main(int argc, char** argv)
{
    cv::namedWindow("lines");
    target.x = 0.0f;
    target.y = 0.0f;

    std::cout << std::fixed << std::setprecision(6);
    setlocale(LC_NUMERIC, "C");

    //////
    /// Open config file
    std::string
        settfilename = argv[2];

    cv::FileStorage
        fs;

    fs.open(settfilename, cv::FileStorage::READ);

    if (!fs.isOpened())
    {
        std::cerr << "Could not open settings file: " << settfilename << std::endl;
        exit(-2);
    }

    nav::EndLineTurnMP mp(fs);

    ros::init(argc, argv, "egocentric_mp");

    ros::NodeHandle n;

    ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>("/Pioneer3at/cmd_vel", 1);
    ros::Subscriber scan_sub = n.subscribe<sensor_msgs::LaserScan>("/Pioneer3AT/laserscan", 1, scan_callback);

    tf::StampedTransform datamatrix_to_base_link;

    tf::TransformListener tfListener;

    geometry_msgs::Twist base_msg;

    float frequency = 33;
    ros::Rate loop_rate(10.0); // 100Hz

    //std::cout << "x;y;phi;r;sigma;theta;curvature;linear;angular;" << std::endl;

    float r = 1.0, theta = 1.57, sigma = 1.57;

    scannedData = cv::Mat::zeros(cv::Size(800,800), CV_8UC1);

    while (ros::ok())
    {
        //float v, omega;
        //v = mp.computeLinearVelocity(r,theta,sigma);
        //omega = mp.computeAngularVelocity(v, r, theta, sigma);

        //std::cout << v << " - " << omega << std::endl;

        //base_msg.linear.x = v;
        //base_msg.angular.z = omega;

        //cmd_vel_pub.publish(base_msg);

        ros::Time a = ros::Time::now();
        ros::Duration offset(0.5);
        ros::Duration timeout(0.15);
        bool isDatamatrix = false;

        while (!isDatamatrix)
        {

            a = ros::Time::now() - offset;
            isDatamatrix = tfListener.waitForTransform("camera_link",
                                                       "datamatrix_frame",
                                                       a,
                                                       timeout);

            ROS_INFO("no transform yet");
        }

        if (isDatamatrix)
        {

            ROS_INFO("Setting target");

            a = ros::Time::now() - offset;
            tfListener.lookupTransform("camera_link",
                                       "datamatrix_frame",
                                       a,
                                       datamatrix_to_base_link);

            tf::Point a,b;

            a.setX(0);
            a.setY(0);
            a.setZ(0);
            b = datamatrix_to_base_link(a);

            target.x = b.x();
            target.y = -1 * b.y();
        }
        else
        {
            ROS_INFO("tf not available");
        }

        ros::spinOnce();
        loop_rate.sleep();
    }

    return 0;
}
<commit_msg>moved lines to robot frame from image frame, try to move the target<commit_after>#include <iostream>
#include <cmath>

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <sensor_msgs/LaserScan.h>
#include <opencv2/opencv.hpp>
#include <tf/tf.h>
#include <tf/transform_listener.h>

#include "endlineturnmp.h"

cv::Mat scannedData;
float factor = 80.0f; // 1mt = 40px
float imageSize = 800;
cv::Point2f center(400,400);
std::vector<cv::Vec4f> lines;

cv::Point2f target, targetDirection;

float pointLineDistance(const cv::Point2f &point, const cv::Vec4i line)
{
    cv::Point pt = point;

    // take 2 points on the line
    cv::Point2f begin(line.val[2],line.val[3]);
    cv::Point2f end(line.val[0],line.val[1]);

    // translate to the origin
    pt = point - begin;
    end = end - begin;

    double area = pt.cross(end);
    return area / cv::norm(end);
}

std::vector<cv::Vec4f> extractLines(const cv::Mat &scannedData)
{
    std::vector<cv::Vec4i> lines_i;
    std::vector<cv::Vec4f> lines_f;
    // Extract lines with the Hough transform
    HoughLinesP( scannedData, lines_i, 2, CV_PI/90, 80, 25, 50 );

    // Move lines from the "image" frame to the robot frame
    for (cv::Vec4f line : lines_i)
    {
        cv::Vec4f new_line_f;
        // Move the first point
        new_line_f.val[0] = (line.val[0] - center.x) / factor;
        new_line_f.val[1] = (line.val[1] - center.y) / factor;
        // Move the first point
        new_line_f.val[2] = (line.val[2] - center.x) / factor;
        new_line_f.val[3] = (line.val[3] - center.y) / factor;

        lines_f.push_back(new_line_f);
    }

    return lines_f;
}

int nearestLineIndex(const cv::Point2f &point, const std::vector<cv::Vec4f> &linesVector)
{
    float minDistance = std::numeric_limits<float>::max();
    int nearestLineIndex = -1;

    int count = 0;
    for (cv::Vec4f line : linesVector)
    {
        // compute line-point distance
        float currentDistance = std::abs(pointLineDistance(point, line));

        //std::cout << currentDistance << " - ";

        if (currentDistance < minDistance)
        {
            nearestLineIndex = count;
            minDistance = currentDistance;
        }

        count = count + 1;
    }

    //std::cout << std::endl << minDistance << std::endl;

    return nearestLineIndex;
}

cv::Point2f getSnapPointToLine(const cv::Point2f &point, const cv::Vec4f line)
{
    float distance = pointLineDistance(point, line);

    cv::Point2f A(line[0],line[1]);

    cv::Vec2f A_pointVector(point - A);

    float A_pointDistance = cv::norm(A_pointVector);

    float A_snapDistance = std::sqrt(A_pointDistance*A_pointDistance - distance*distance);

    cv::Vec2f lineVector(line[2]-line[0], line[3]-line[1]);
    lineVector = A_snapDistance * cv::normalize(lineVector);

    return cv::Point2f(A.x + lineVector.val[0], A.y + lineVector.val[1]);
}

void morphClosure(const cv::Mat &src, cv::Mat &dst)
{
    int dilationType = cv::MORPH_RECT;
    int dilationSize = 3;
    cv::Mat element = getStructuringElement( dilationType,
                                         cv::Size( 2*dilationSize + 1, 2*dilationSize+1 ),
                                         cv::Point( dilationSize, dilationSize ) );
    cv::Mat tmp;
    cv::dilate(src, tmp, element);
    cv::erode(tmp, dst, element);
}

void scan_callback(const sensor_msgs::LaserScan::ConstPtr &msg)
{
    // get laser points
    scannedData = cv::Mat::zeros(cv::Size(800,800), CV_8UC1);

    float angle = msg->angle_min;
    for (int i = 0; i < msg->ranges.size(); i++)
    {
        float range = msg->ranges[i];
        if (range >= msg->range_min && range <= msg->range_max)
        {
            cv::Point2f pt(range * std::cos(angle), -1 * range * std::sin(angle));
            scannedData.at<uchar>(pt*factor + center) = 255;
        }

        angle = angle + msg->angle_increment;
    }

    // perform a closure (dilation+erosion)
    cv::Mat closedScannedData;
    morphClosure(scannedData, closedScannedData);

    // extract lines from laser points
    lines = extractLines(closedScannedData);

    // get nearest line index
    int idx = nearestLineIndex(target, lines);
    float distance = pointLineDistance(target, lines[idx]);

    //std::cout << idx << " - (" << target.x << "," << target.y << ")" << " - " << lines[idx] << " - " << distance << std::endl;

    // plot lines, points...
    cv::Mat linesColor;
    cv::cvtColor(scannedData, linesColor, CV_GRAY2BGR);

    cv::circle(linesColor, cv::Point2f(0,0)*factor+center, 3, cv::Scalar(200,200,200),3);
    for( size_t i = 0; i < lines.size(); i++ )
    {
      cv::Vec4f l = lines[i];
      cv::line( linesColor,
                cv::Point2f(l[0], l[1])*factor+center,
                cv::Point2f(l[2], l[3])*factor+center,
                i==idx?cv::Scalar(0,255,0):cv::Scalar(0,0,255),
                1, CV_AA );
      cv::circle(linesColor, cv::Point2f(l[0], l[1])*factor+center, 2, cv::Scalar(200,200,100));
      cv::circle(linesColor, cv::Point2f(l[2], l[3])*factor+center, 2, cv::Scalar(100,200,200));
    }

    cv::circle(linesColor, target*factor+center, 3, cv::Scalar(250,50,250),3);

    // snap target to the nearest line
    target = getSnapPointToLine(target, lines[idx]);

    cv::circle(linesColor, target*factor+center, 3, cv::Scalar(255,0,0),3);

    /// Move the target 1mt in front of the line
    // get an ortogonal vector
    cv::Vec2f lineVector((lines[idx])[0] - (lines[idx])[2],
                         (lines[idx])[1] - (lines[idx])[3]);
    /// TODO: check 0 values...
    cv::Vec2f ortoLineVector(-lineVector.val[1], lineVector.val[0]);
    ortoLineVector = ortoLineVector / cv::norm(ortoLineVector);

    target = target + cv::Point2f(ortoLineVector[0], ortoLineVector[1]);

    cv::circle(linesColor, target*factor+center, 3, cv::Scalar(0,255,0),3);

    std::cout << "target distance: " << cv::norm(target) << std::endl;

    cv::imshow("lines", linesColor);
    cv::waitKey(33);
}

int main(int argc, char** argv)
{
    cv::namedWindow("lines");
    target.x = 0.0f;
    target.y = 0.0f;

    std::cout << std::fixed << std::setprecision(6);
    setlocale(LC_NUMERIC, "C");

    //////
    /// Open config file
    std::string
        settfilename = argv[2];

    cv::FileStorage
        fs;

    fs.open(settfilename, cv::FileStorage::READ);

    if (!fs.isOpened())
    {
        std::cerr << "Could not open settings file: " << settfilename << std::endl;
        exit(-2);
    }

    nav::EndLineTurnMP mp(fs);

    ros::init(argc, argv, "egocentric_mp");

    ros::NodeHandle n;

    ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>("/Pioneer3at/cmd_vel", 1);
    ros::Subscriber scan_sub = n.subscribe<sensor_msgs::LaserScan>("/Pioneer3AT/laserscan", 1, scan_callback);

    tf::StampedTransform datamatrix_to_base_link;

    tf::TransformListener tfListener;

    geometry_msgs::Twist base_msg;

    float frequency = 33;
    ros::Rate loop_rate(10.0); // 100Hz

    //std::cout << "x;y;phi;r;sigma;theta;curvature;linear;angular;" << std::endl;

    float r = 1.0, theta = 1.57, sigma = 1.57;

    scannedData = cv::Mat::zeros(cv::Size(800,800), CV_8UC1);

    while (ros::ok())
    {
        //float v, omega;
        //v = mp.computeLinearVelocity(r,theta,sigma);
        //omega = mp.computeAngularVelocity(v, r, theta, sigma);

        //std::cout << v << " - " << omega << std::endl;

        //base_msg.linear.x = v;
        //base_msg.angular.z = omega;

        //cmd_vel_pub.publish(base_msg);

        ros::Time a = ros::Time::now();
        ros::Duration offset(0.5);
        ros::Duration timeout(0.15);
        bool isDatamatrix = false;

        while (!isDatamatrix)
        {

            a = ros::Time::now() - offset;
            isDatamatrix = tfListener.waitForTransform("camera_link",
                                                       "datamatrix_frame",
                                                       a,
                                                       timeout);

            ROS_INFO("no transform yet");
        }

        if (isDatamatrix)
        {

            ROS_INFO("Setting target");

            a = ros::Time::now() - offset;
            tfListener.lookupTransform("camera_link",
                                       "datamatrix_frame",
                                       a,
                                       datamatrix_to_base_link);

            tf::Point a,b;

            a.setX(0);
            a.setY(0);
            a.setZ(0);
            b = datamatrix_to_base_link(a);

            target.x = b.x();
            target.y = -1 * b.y();
        }
        else
        {
            ROS_INFO("tf not available");
        }

        ros::spinOnce();
        loop_rate.sleep();
    }

    return 0;
}
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_CORE_JNIERRORCATEGORY_HPP
#define OUZEL_CORE_JNIERRORCATEGORY_HPP

#include <system_error>
#include <jni.h>

namespace ouzel::core::android
{
    class ErrorCategory final: public std::error_category
    {
    public:
        const char* name() const noexcept final
        {
            return "JNI";
        }

        std::string message(int condition) const final
        {
            switch (condition)
            {
                case JNI_ERR: return "JNI_ERR";
                case JNI_EDETACHED: return "JNI_EDETACHED";
                case JNI_EVERSION: return "JNI_EVERSION";
                default: return "Unknown error (" + std::to_string(condition) + ")";
            }
        }
    };
}

#endif // OUZEL_CORE_JNIERRORCATEGORY_HPP
<commit_msg>Add the missing errors to JNI error category<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_CORE_JNIERRORCATEGORY_HPP
#define OUZEL_CORE_JNIERRORCATEGORY_HPP

#include <system_error>
#include <jni.h>

namespace ouzel::core::android
{
    class ErrorCategory final: public std::error_category
    {
    public:
        const char* name() const noexcept final
        {
            return "JNI";
        }

        std::string message(int condition) const final
        {
            switch (condition)
            {
                case JNI_ERR: return "JNI_ERR";
                case JNI_EDETACHED: return "JNI_EDETACHED";
                case JNI_EVERSION: return "JNI_EVERSION";
                case JNI_ENOMEM: return "JNI_ENOMEM";
                case JNI_EEXIST: return "JNI_EEXIST";
                case JNI_EINVAL: return "JNI_EINVAL";
                default: return "Unknown error (" + std::to_string(condition) + ")";
            }
        }
    };
}

#endif // OUZEL_CORE_JNIERRORCATEGORY_HPP
<|endoftext|>


<commit_before>/*****************************************************************************

YASK: Yet Another Stencil Kernel
Copyright (c) 2014-2018, Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

* The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

*****************************************************************************/

// Base class for defining stencil equations.

#include "Print.hpp"
#include "CppIntrin.hpp"

using namespace std;

namespace yask {

    // Stencil-solution APIs.
    yc_grid_ptr StencilSolution::newGrid(const std::string& name,
                                         bool isScratch,
                                         const std::vector<yc_index_node_ptr>& dims) {

        // Make new grid and add to solution.
        // TODO: fix this mem leak--make smart ptr.

        // Copy pointers to concrete type.
        IndexExprPtrVec dims2;
        for (auto d : dims) {
            auto d2 = dynamic_pointer_cast<IndexExpr>(d);
            assert(d2);
            dims2.push_back(d2);
        }

        auto* gp = new Grid(name, isScratch, this, dims2);
        assert(gp);
        return gp;
    }

    // Stencil-solution APIs.
    void StencilSolution::set_fold_len(const yc_index_node_ptr dim,
                                       int len) {
        auto& fold = _settings._foldOptions;
        fold.addDimBack(dim->get_name(), len);
    }
    void StencilSolution::set_cluster_mult(const yc_index_node_ptr dim,
                                           int mult) {
        auto& cluster = _settings._clusterOptions;
        cluster.addDimBack(dim->get_name(), mult);
    }

    // Create the intermediate data for printing.
    void StencilSolution::analyze_solution(int vlen,
                                           bool is_folding_efficient) {

        // Call the stencil 'define' method to create ASTs.
        // ASTs can also be created via the APIs.
        define();

        // Find all the stencil dimensions from the grids.
        // Create the final folds and clusters from the cmd-line options.
        _dims.setDims(_grids, _settings, vlen, is_folding_efficient, *_dos);

        // Determine which grids can be folded.
        _grids.setFolding(_dims);

        // Determine which grid points can be vectorized and analyze inner-loop accesses.
        _eqs.analyzeVec(_dims);
        _eqs.analyzeLoop(_dims);

        // Find dependencies between equations.
        EqDepMap eq_deps;
        _eqs.findDeps(_dims, &eq_deps, *_dos);

        // Update access stats for the grids.
        _eqs.updateGridStats(eq_deps);

        // Create equation groups based on dependencies and/or target strings.
        _eqGroups.set_basename_default(_settings._eq_group_basename_default);
        _eqGroups.set_dims(_dims);
        _eqGroups.makeEqGroups(_eqs, _settings._eqGroupTargets, eq_deps, *_dos);
        _eqGroups.optimizeEqGroups(_settings, "scalar & vector", false, *_dos);
        assert(_dims._stepDir);

        // Make a copy of each equation at each cluster offset.
        // We will use these for inter-cluster optimizations and code generation.
        *_dos << "Constructing cluster of equations containing " <<
            _dims._clusterMults.product() << " vector(s)...\n";
        _clusterEqGroups = _eqGroups;
        _clusterEqGroups.replicateEqsInCluster(_dims);
        if (_settings._doOptCluster)
            _clusterEqGroups.optimizeEqGroups(_settings, "cluster", true, *_dos);
    }

    // Format in given format-type.
    void StencilSolution::format(const string& format_type,
                                 yask_output_ptr output) {

        // Look for format match.
        // Most args to the printers just set references to data.
        // Data itself will be created in analyze_solution().
        PrinterBase* printer = 0;
        if (format_type == "cpp")
            printer = new YASKCppPrinter(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "knc")
            printer = new YASKKncPrinter(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "avx" || format_type == "avx2")
            printer = new YASKAvx256Printer(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "avx512" || format_type == "avx512f")
            printer = new YASKAvx512Printer(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "dot")
            printer = new DOTPrinter(*this, _clusterEqGroups, false);
        else if (format_type == "dot-lite")
            printer = new DOTPrinter(*this, _clusterEqGroups, true);
        else if (format_type == "pseudo")
            printer = new PseudoPrinter(*this, _clusterEqGroups);
        else if (format_type == "pov-ray") // undocumented.
            printer = new POVRayPrinter(*this, _clusterEqGroups);
        else {
            THROW_YASK_EXCEPTION("Error: format-type '" << format_type <<
                                 "' is not recognized");
        }
        assert(printer);
        int vlen = printer->num_vec_elems();
        bool is_folding_efficient = printer->is_folding_efficient();

        // Set data for equation groups, dims, etc.
        analyze_solution(vlen, is_folding_efficient);

        // Create the output.
        *_dos << "Generating '" << format_type << "' output...\n";
        printer->print(output->get_ostream());
        delete printer;
    }

} // namespace yask.

<commit_msg>Remove assertion causing Devito test failure.<commit_after>/*****************************************************************************

YASK: Yet Another Stencil Kernel
Copyright (c) 2014-2018, Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

* The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

*****************************************************************************/

// Base class for defining stencil equations.

#include "Print.hpp"
#include "CppIntrin.hpp"

using namespace std;

namespace yask {

    // Stencil-solution APIs.
    yc_grid_ptr StencilSolution::newGrid(const std::string& name,
                                         bool isScratch,
                                         const std::vector<yc_index_node_ptr>& dims) {

        // Make new grid and add to solution.
        // TODO: fix this mem leak--make smart ptr.

        // Copy pointers to concrete type.
        IndexExprPtrVec dims2;
        for (auto d : dims) {
            auto d2 = dynamic_pointer_cast<IndexExpr>(d);
            assert(d2);
            dims2.push_back(d2);
        }

        auto* gp = new Grid(name, isScratch, this, dims2);
        assert(gp);
        return gp;
    }

    // Stencil-solution APIs.
    void StencilSolution::set_fold_len(const yc_index_node_ptr dim,
                                       int len) {
        auto& fold = _settings._foldOptions;
        fold.addDimBack(dim->get_name(), len);
    }
    void StencilSolution::set_cluster_mult(const yc_index_node_ptr dim,
                                           int mult) {
        auto& cluster = _settings._clusterOptions;
        cluster.addDimBack(dim->get_name(), mult);
    }

    // Create the intermediate data for printing.
    void StencilSolution::analyze_solution(int vlen,
                                           bool is_folding_efficient) {

        // Call the stencil 'define' method to create ASTs.
        // ASTs can also be created via the APIs.
        define();

        // Find all the stencil dimensions from the grids.
        // Create the final folds and clusters from the cmd-line options.
        _dims.setDims(_grids, _settings, vlen, is_folding_efficient, *_dos);

        // Determine which grids can be folded.
        _grids.setFolding(_dims);

        // Determine which grid points can be vectorized and analyze inner-loop accesses.
        _eqs.analyzeVec(_dims);
        _eqs.analyzeLoop(_dims);

        // Find dependencies between equations.
        EqDepMap eq_deps;
        _eqs.findDeps(_dims, &eq_deps, *_dos);

        // Update access stats for the grids.
        _eqs.updateGridStats(eq_deps);

        // Create equation groups based on dependencies and/or target strings.
        _eqGroups.set_basename_default(_settings._eq_group_basename_default);
        _eqGroups.set_dims(_dims);
        _eqGroups.makeEqGroups(_eqs, _settings._eqGroupTargets, eq_deps, *_dos);
        _eqGroups.optimizeEqGroups(_settings, "scalar & vector", false, *_dos);

        // Make a copy of each equation at each cluster offset.
        // We will use these for inter-cluster optimizations and code generation.
        *_dos << "Constructing cluster of equations containing " <<
            _dims._clusterMults.product() << " vector(s)...\n";
        _clusterEqGroups = _eqGroups;
        _clusterEqGroups.replicateEqsInCluster(_dims);
        if (_settings._doOptCluster)
            _clusterEqGroups.optimizeEqGroups(_settings, "cluster", true, *_dos);
    }

    // Format in given format-type.
    void StencilSolution::format(const string& format_type,
                                 yask_output_ptr output) {

        // Look for format match.
        // Most args to the printers just set references to data.
        // Data itself will be created in analyze_solution().
        PrinterBase* printer = 0;
        if (format_type == "cpp")
            printer = new YASKCppPrinter(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "knc")
            printer = new YASKKncPrinter(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "avx" || format_type == "avx2")
            printer = new YASKAvx256Printer(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "avx512" || format_type == "avx512f")
            printer = new YASKAvx512Printer(*this, _eqGroups, _clusterEqGroups, &_dims);
        else if (format_type == "dot")
            printer = new DOTPrinter(*this, _clusterEqGroups, false);
        else if (format_type == "dot-lite")
            printer = new DOTPrinter(*this, _clusterEqGroups, true);
        else if (format_type == "pseudo")
            printer = new PseudoPrinter(*this, _clusterEqGroups);
        else if (format_type == "pov-ray") // undocumented.
            printer = new POVRayPrinter(*this, _clusterEqGroups);
        else {
            THROW_YASK_EXCEPTION("Error: format-type '" << format_type <<
                                 "' is not recognized");
        }
        assert(printer);
        int vlen = printer->num_vec_elems();
        bool is_folding_efficient = printer->is_folding_efficient();

        // Set data for equation groups, dims, etc.
        analyze_solution(vlen, is_folding_efficient);

        // Create the output.
        *_dos << "Generating '" << format_type << "' output...\n";
        printer->print(output->get_ostream());
        delete printer;
    }

} // namespace yask.

<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdlib>
#include <memory>
#include <functional>
#include <vector>
#include <iostream>
#include "fruit/impl/metaprogramming.h"
#include "fruit/impl/demangle_type_name.h"
#include "fruit/impl/type_info.h"

#include "fruit/impl/component_storage.h"

using std::cout;
using std::endl;

namespace {

std::string multipleBindingsError(fruit::impl::TypeIndex typeIndex) {
  return "the type " + demangleTypeName(typeIndex.name()) + " was provided more than once, with different bindings.\n"
        + "This was not caught at compile time because at least one of the involved components bound this type but didn't expose it in the component signature.\n"
        + "If the type has a default constructor or an Inject annotation, this problem may arise even if this type is bound/provided by only one component (and then hidden), if this type is auto-injected in another component.\n"
        + "If the source of the problem is unclear, try exposing this type in all the component signatures where it's bound; if no component hides it this can't happen.\n";
}

} // namespace

namespace fruit {
namespace impl {

bool ComponentStorage::TypeInfo::operator<(const TypeInfo& other) const {
  // `destroy' is intentionally not compared.
  // If the others are equal it should also be equal. If it isn't, the two TypeInfo structs
  // are still equivalent because they produce the same injected object.
  return std::tie(      storedSingleton,       create,       createArgument)
       < std::tie(other.storedSingleton, other.create, other.createArgument);
}

void ComponentStorage::ensureConstructed(TypeIndex typeIndex, TypeInfo& typeInfo) {
  if (typeInfo.storedSingleton == nullptr) {
    FruitCheck(bool(typeInfo.create), [=](){return "attempting to create an instance for the type " + demangleTypeName(typeIndex.name()) + " but there is no create operation";});
    typeInfo.storedSingleton = typeInfo.create(*this, typeInfo.createArgument);
    // This can happen if the user-supplied provider returns nullptr.
    check(typeInfo.storedSingleton != nullptr, [=](){return "attempting to get an instance for the type " + demangleTypeName(typeIndex.name()) + " but got nullptr";});

    createdSingletons.push_back(typeIndex);
  }
}

void ComponentStorage::ensureConstructedMultibinding(TypeIndex typeIndex, std::set<TypeInfo>& typeInfos) {
  bool allSingletonsCreated = true;
  for (const TypeInfo& typeInfo : typeInfos) {
    if (typeInfo.storedSingleton == nullptr) {
      allSingletonsCreated = false;
    }
  }
  if (!allSingletonsCreated) {
    // When we construct a singleton in a TypeInfo we change the order, so we can't do it for typeInfos already in a set.
    // We need to create a new set.
    std::set<TypeInfo> newTypeInfos;
    for (TypeInfo typeInfo : typeInfos) {
      if (typeInfo.storedSingleton == nullptr) {
        FruitCheck(bool(typeInfo.create), [=](){return "attempting to create an instance for the type " + demangleTypeName(typeIndex.name()) + " but there is no create operation";});
        typeInfo.storedSingleton = typeInfo.create(*this, typeInfo.createArgument);
        // This can happen if the user-supplied provider returns nullptr.
        check(typeInfo.storedSingleton != nullptr, [=](){return "attempting to get an instance for the type " + demangleTypeName(typeIndex.name()) + " but got nullptr";});
      }
      newTypeInfos.insert(typeInfo);
    }
    std::swap(typeInfos, newTypeInfos);
  }
}

void* ComponentStorage::getPtr(TypeIndex typeIndex) {
#ifdef FRUIT_EXTRA_DEBUG
  std::cerr << "In ComponentStorage::getPtr(" << demangleTypeName(typeIndex.name()) << ")" << std::endl;
#endif
  for (ComponentStorage* storage = this; storage != nullptr; storage = storage->parent) {
    auto itr = storage->typeRegistry.find(typeIndex);
    if (itr == storage->typeRegistry.end()) {
      // Not registered here, try the parents (if any).
      continue;
    }
    storage->ensureConstructed(typeIndex, itr->second);
    return itr->second.storedSingleton;
  }
  FruitCheck(false, [=](){return "attempting to getPtr() on a non-registered type: " + demangleTypeName(typeIndex.name());});
  // Never executed.
  return nullptr;
}

void ComponentStorage::printError(const std::string& message) {
  cout << "Fatal injection error: " << message << endl;
  cout << "Registered types:" << endl;
  for (auto typePair : typeRegistry) {
    std::cout << demangleTypeName(typePair.first.name()) << std::endl;
  }
  std::cout << std::endl;
  if (parent != nullptr) {
    for (ComponentStorage* storage = parent; storage != nullptr; storage = storage->parent) {
      cout << "Registered types in parent injector:" << endl;
      for (auto typePair : storage->typeRegistry) {
        std::cout << demangleTypeName(typePair.first.name()) << std::endl;
      }
      for (auto typePair : storage->typeRegistryForMultibindings) {
        std::cout << demangleTypeName(typePair.first.name()) << " (multibinding)" << std::endl;
      }
      std::cout << std::endl;
    }
  }
}

void ComponentStorage::install(const ComponentStorage& other) {
  FruitCheck(other.createdSingletons.empty(), "Attempting to install a component that has already started creating instances");
  FruitCheck(other.parent == nullptr, "Attempting to install a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to install a component after calling setParent().");
  for (const auto& typeInfoPair : other.typeRegistry) {
    TypeIndex typeIndex = typeInfoPair.first;
    const TypeInfo& theirInfo = typeInfoPair.second;
    if (theirInfo.storedSingleton == nullptr) {
      createTypeInfo(typeIndex, theirInfo.create, theirInfo.createArgument, theirInfo.destroy);
    } else {
      createTypeInfo(typeIndex, theirInfo.storedSingleton, theirInfo.destroy);
    }
  }
  for (const auto& typeInfoPair : other.typeRegistryForMultibindings) {
    TypeIndex typeIndex = typeInfoPair.first;
    for (const TypeInfo& theirInfo : typeInfoPair.second) {
      if (theirInfo.storedSingleton == nullptr) {
        createTypeInfoForMultibinding(typeIndex, theirInfo.create, theirInfo.createArgument, theirInfo.destroy);
      } else {
        createTypeInfoForMultibinding(typeIndex, theirInfo.storedSingleton, theirInfo.destroy);
      }
    }
  }
}

void ComponentStorage::clear() {
  for (auto i = createdSingletons.rbegin(), i_end = createdSingletons.rend(); i != i_end; ++i) {
    std::unordered_map<TypeIndex, TypeInfo>::iterator itr = typeRegistry.find(*i);
    FruitCheck(itr != typeRegistry.end(), "internal error: attempting to destroy an non-registered type");
    TypeInfo& typeInfo = itr->second;
    // Note: if this was a binding or user-provided object, the object is NOT destroyed.
    if (typeInfo.storedSingleton != nullptr) {
      typeInfo.destroy(typeInfo.storedSingleton);
    }
  }
  // Multibindings can depend on bindings, but not vice-versa and they also can't depend on other multibindings.
  // Delete them in any order.
  for (auto& elem : typeRegistryForMultibindings) {
    std::set<TypeInfo>& typeInfos = elem.second;
    for (const TypeInfo& typeInfo : typeInfos) {
      if (typeInfo.storedSingleton != nullptr) {
        typeInfo.destroy(typeInfo.storedSingleton);
      }
    }
  }
  createdSingletons.clear();
  typeRegistry.clear();
  typeRegistryForMultibindings.clear();
}

ComponentStorage::~ComponentStorage() {
  clear();
}

ComponentStorage& ComponentStorage::operator=(const ComponentStorage& other) {
  // Can't copy the component once it starts owning resources (singleton instances).
  FruitCheck(other.createdSingletons.empty(), "Attempting to copy a component that has already started creating instances");
  ComponentStorage tmp(other);
  swap(tmp);
  return *this;
}

ComponentStorage& ComponentStorage::operator=(ComponentStorage&& other) {
  swap(other);
  return *this;
}

void ComponentStorage::createTypeInfo(TypeIndex typeIndex,
                                      void* (*create)(ComponentStorage&, void*),
                                      void* createArgument,
                                      void (*destroy)(void*)) {
#ifdef FRUIT_EXTRA_DEBUG
  std::cerr << "In ComponentStorage::createTypeInfo for type " << demangleTypeName(typeIndex.name()) << std::endl;
#endif
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");
  auto itr = typeRegistry.find(typeIndex);
  if (itr == typeRegistry.end()) {
    // This type wasn't registered yet, register it.
    TypeInfo& typeInfo = typeRegistry[typeIndex];
    typeInfo.create = create;
    typeInfo.createArgument = createArgument;
    typeInfo.storedSingleton = nullptr;
    typeInfo.destroy = destroy;
  } else {
    // This type was already registered.
    TypeInfo& ourInfo = itr->second;
    check(ourInfo.storedSingleton == nullptr, [=](){ return multipleBindingsError(typeIndex); });
    // At this point it's guaranteed that ourInfo.storedSingleton==nullptr.
    // If the create operations and arguments are equal ok, but if they aren't we
    // can't be sure that they're equivalent and we abort.
    bool equal = ourInfo.create == create
              && ourInfo.createArgument == createArgument;
    check(equal, [=](){ return multipleBindingsError(typeIndex); });
  }
}

void ComponentStorage::createTypeInfo(TypeIndex typeIndex,
                                      void* storedSingleton,
                                      void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");
  auto itr = typeRegistry.find(typeIndex);
  if (itr == typeRegistry.end()) {
    // This type wasn't registered yet, register it.
    TypeInfo& typeInfo = typeRegistry[typeIndex];
    typeInfo.storedSingleton = storedSingleton;
    typeInfo.create = nullptr;
    typeInfo.createArgument = nullptr;
    typeInfo.destroy = destroy;
  } else {
    // This type was already registered.
    TypeInfo& ourInfo = itr->second;
    check(ourInfo.storedSingleton != nullptr, [=](){ return multipleBindingsError(typeIndex); });
    // At this point it's guaranteed that ourInfo.storedSingleton!=nullptr.
    // If the stored singletons and destroy operations are equal ok, but if they aren't we
    // can't be sure that they're equivalent and we abort.
    bool equal = ourInfo.storedSingleton == storedSingleton;
    check(equal, [=](){ return multipleBindingsError(typeIndex); });
  }
}

void ComponentStorage::createTypeInfoForMultibinding(TypeIndex typeIndex,
                                                  void* (*create)(ComponentStorage&, void*),
                                                  void* createArgument,
                                                  void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");

  TypeInfo typeInfo;
  typeInfo.create = create;
  typeInfo.createArgument = createArgument;
  typeInfo.storedSingleton = nullptr;
  typeInfo.destroy = destroy;

  // This works no matter whether typeIndex was registered as a multibinding before or not.
  typeRegistryForMultibindings[typeIndex].insert(typeInfo);
}

void ComponentStorage::createTypeInfoForMultibinding(TypeIndex typeIndex,
                                                  void* storedSingleton,
                                                  void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");

  TypeInfo typeInfo;
  typeInfo.storedSingleton = storedSingleton;
  typeInfo.create = nullptr;
  typeInfo.createArgument = nullptr;
  typeInfo.destroy = destroy;

  typeRegistryForMultibindings[typeIndex].insert(typeInfo);
}

void ComponentStorage::setParent(ComponentStorage* parent) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  this->parent = parent;
  // If the same type is bound in a parent and the child, ensure that the bindings are equivalent and
  // remove the bound in the child.
  for (ComponentStorage* p = parent; p != nullptr; p = p->parent) {
    for (auto i = typeRegistry.begin(), i_end = typeRegistry.end(); i != i_end; /* no increment */) {
      TypeIndex typeIndex = i->first;
      const TypeInfo& childInfo = i->second;
      auto itr = p->typeRegistry.find(typeIndex);
      if (itr == p->typeRegistry.end()) {
        // Type not bound in parent, ok.
        ++i;
        continue;
      }
      const TypeInfo& parentInfo = itr->second;

      // This type was already registered.

      bool equal;
      if (childInfo.storedSingleton != nullptr) {
        // Instance binding.
        equal = childInfo.storedSingleton == parentInfo.storedSingleton;
      } else {
        // Note that parentInfo.storedSingleton may or may not be nullptr.
        equal = childInfo.create == parentInfo.create
            && childInfo.createArgument == parentInfo.createArgument;
      }

      check(equal, [=](){ return multipleBindingsError(typeIndex); });

      i = typeRegistry.erase(i);
    }

    for (auto typeIndexInfoPair : typeRegistryForMultibindings) {
      TypeIndex typeIndex = typeIndexInfoPair.first;
      std::set<TypeInfo>& typeInfos = typeIndexInfoPair.second;
      for (auto i = typeInfos.begin(), i_end = typeInfos.end(); i != i_end; /* no increment */) {
        const TypeInfo& childInfo = *i;
        auto itr = p->typeRegistryForMultibindings.find(typeIndex);
        if (itr == p->typeRegistryForMultibindings.end()) {
          // No multibinding in parent for this type, keep it.
          ++i;
          continue;
        }
        std::set<TypeInfo>& parentInfos = itr->second;
        if (parentInfos.count(childInfo) == 0) {
          // The parent doesn't have the same multibinding, keep it.
          ++i;
          continue;
        }

        // Same multibinding found in parent, remove it from the child.
        i = typeInfos.erase(i);
      }
    }
  }
}

void ComponentStorage::eagerlyInjectMultibindings() {
  for (ComponentStorage* p = this; p != nullptr; p = p->parent) {
    for (auto typeIndexInfoPair : p->typeRegistryForMultibindings) {
      p->ensureConstructedMultibinding(typeIndexInfoPair.first, typeIndexInfoPair.second);
    }
  }
}

} // namespace impl
} // namespace fruit
<commit_msg>Fix a bug in ComponentStorage where multibindings were deleted after the bindings that they depended on.<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdlib>
#include <memory>
#include <functional>
#include <vector>
#include <iostream>
#include "fruit/impl/metaprogramming.h"
#include "fruit/impl/demangle_type_name.h"
#include "fruit/impl/type_info.h"

#include "fruit/impl/component_storage.h"

using std::cout;
using std::endl;

namespace {

std::string multipleBindingsError(fruit::impl::TypeIndex typeIndex) {
  return "the type " + demangleTypeName(typeIndex.name()) + " was provided more than once, with different bindings.\n"
        + "This was not caught at compile time because at least one of the involved components bound this type but didn't expose it in the component signature.\n"
        + "If the type has a default constructor or an Inject annotation, this problem may arise even if this type is bound/provided by only one component (and then hidden), if this type is auto-injected in another component.\n"
        + "If the source of the problem is unclear, try exposing this type in all the component signatures where it's bound; if no component hides it this can't happen.\n";
}

} // namespace

namespace fruit {
namespace impl {

bool ComponentStorage::TypeInfo::operator<(const TypeInfo& other) const {
  // `destroy' is intentionally not compared.
  // If the others are equal it should also be equal. If it isn't, the two TypeInfo structs
  // are still equivalent because they produce the same injected object.
  return std::tie(      storedSingleton,       create,       createArgument)
       < std::tie(other.storedSingleton, other.create, other.createArgument);
}

void ComponentStorage::ensureConstructed(TypeIndex typeIndex, TypeInfo& typeInfo) {
  if (typeInfo.storedSingleton == nullptr) {
    FruitCheck(bool(typeInfo.create), [=](){return "attempting to create an instance for the type " + demangleTypeName(typeIndex.name()) + " but there is no create operation";});
    typeInfo.storedSingleton = typeInfo.create(*this, typeInfo.createArgument);
    // This can happen if the user-supplied provider returns nullptr.
    check(typeInfo.storedSingleton != nullptr, [=](){return "attempting to get an instance for the type " + demangleTypeName(typeIndex.name()) + " but got nullptr";});

    createdSingletons.push_back(typeIndex);
  }
}

void ComponentStorage::ensureConstructedMultibinding(TypeIndex typeIndex, std::set<TypeInfo>& typeInfos) {
  bool allSingletonsCreated = true;
  for (const TypeInfo& typeInfo : typeInfos) {
    if (typeInfo.storedSingleton == nullptr) {
      allSingletonsCreated = false;
    }
  }
  if (!allSingletonsCreated) {
    // When we construct a singleton in a TypeInfo we change the order, so we can't do it for typeInfos already in a set.
    // We need to create a new set.
    std::set<TypeInfo> newTypeInfos;
    for (TypeInfo typeInfo : typeInfos) {
      if (typeInfo.storedSingleton == nullptr) {
        FruitCheck(bool(typeInfo.create), [=](){return "attempting to create an instance for the type " + demangleTypeName(typeIndex.name()) + " but there is no create operation";});
        typeInfo.storedSingleton = typeInfo.create(*this, typeInfo.createArgument);
        // This can happen if the user-supplied provider returns nullptr.
        check(typeInfo.storedSingleton != nullptr, [=](){return "attempting to get an instance for the type " + demangleTypeName(typeIndex.name()) + " but got nullptr";});
      }
      newTypeInfos.insert(typeInfo);
    }
    std::swap(typeInfos, newTypeInfos);
  }
}

void* ComponentStorage::getPtr(TypeIndex typeIndex) {
#ifdef FRUIT_EXTRA_DEBUG
  std::cerr << "In ComponentStorage::getPtr(" << demangleTypeName(typeIndex.name()) << ")" << std::endl;
#endif
  for (ComponentStorage* storage = this; storage != nullptr; storage = storage->parent) {
    auto itr = storage->typeRegistry.find(typeIndex);
    if (itr == storage->typeRegistry.end()) {
      // Not registered here, try the parents (if any).
      continue;
    }
    storage->ensureConstructed(typeIndex, itr->second);
    return itr->second.storedSingleton;
  }
  FruitCheck(false, [=](){return "attempting to getPtr() on a non-registered type: " + demangleTypeName(typeIndex.name());});
  // Never executed.
  return nullptr;
}

void ComponentStorage::printError(const std::string& message) {
  cout << "Fatal injection error: " << message << endl;
  cout << "Registered types:" << endl;
  for (auto typePair : typeRegistry) {
    std::cout << demangleTypeName(typePair.first.name()) << std::endl;
  }
  std::cout << std::endl;
  if (parent != nullptr) {
    for (ComponentStorage* storage = parent; storage != nullptr; storage = storage->parent) {
      cout << "Registered types in parent injector:" << endl;
      for (auto typePair : storage->typeRegistry) {
        std::cout << demangleTypeName(typePair.first.name()) << std::endl;
      }
      for (auto typePair : storage->typeRegistryForMultibindings) {
        std::cout << demangleTypeName(typePair.first.name()) << " (multibinding)" << std::endl;
      }
      std::cout << std::endl;
    }
  }
}

void ComponentStorage::install(const ComponentStorage& other) {
  FruitCheck(other.createdSingletons.empty(), "Attempting to install a component that has already started creating instances");
  FruitCheck(other.parent == nullptr, "Attempting to install a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to install a component after calling setParent().");
  for (const auto& typeInfoPair : other.typeRegistry) {
    TypeIndex typeIndex = typeInfoPair.first;
    const TypeInfo& theirInfo = typeInfoPair.second;
    if (theirInfo.storedSingleton == nullptr) {
      createTypeInfo(typeIndex, theirInfo.create, theirInfo.createArgument, theirInfo.destroy);
    } else {
      createTypeInfo(typeIndex, theirInfo.storedSingleton, theirInfo.destroy);
    }
  }
  for (const auto& typeInfoPair : other.typeRegistryForMultibindings) {
    TypeIndex typeIndex = typeInfoPair.first;
    for (const TypeInfo& theirInfo : typeInfoPair.second) {
      if (theirInfo.storedSingleton == nullptr) {
        createTypeInfoForMultibinding(typeIndex, theirInfo.create, theirInfo.createArgument, theirInfo.destroy);
      } else {
        createTypeInfoForMultibinding(typeIndex, theirInfo.storedSingleton, theirInfo.destroy);
      }
    }
  }
}

void ComponentStorage::clear() {
  // Multibindings can depend on bindings, but not vice-versa and they also can't depend on other multibindings.
  // Delete them in any order.
  for (auto& elem : typeRegistryForMultibindings) {
    std::set<TypeInfo>& typeInfos = elem.second;
    for (const TypeInfo& typeInfo : typeInfos) {
      if (typeInfo.storedSingleton != nullptr) {
        typeInfo.destroy(typeInfo.storedSingleton);
      }
    }
  }

  for (auto i = createdSingletons.rbegin(), i_end = createdSingletons.rend(); i != i_end; ++i) {
    std::unordered_map<TypeIndex, TypeInfo>::iterator itr = typeRegistry.find(*i);
    FruitCheck(itr != typeRegistry.end(), "internal error: attempting to destroy an non-registered type");
    TypeInfo& typeInfo = itr->second;
    // Note: if this was a binding or user-provided object, the object is NOT destroyed.
    if (typeInfo.storedSingleton != nullptr) {
      typeInfo.destroy(typeInfo.storedSingleton);
    }
  }
  createdSingletons.clear();
  typeRegistry.clear();
  typeRegistryForMultibindings.clear();
}

ComponentStorage::~ComponentStorage() {
  clear();
}

ComponentStorage& ComponentStorage::operator=(const ComponentStorage& other) {
  // Can't copy the component once it starts owning resources (singleton instances).
  FruitCheck(other.createdSingletons.empty(), "Attempting to copy a component that has already started creating instances");
  ComponentStorage tmp(other);
  swap(tmp);
  return *this;
}

ComponentStorage& ComponentStorage::operator=(ComponentStorage&& other) {
  swap(other);
  return *this;
}

void ComponentStorage::createTypeInfo(TypeIndex typeIndex,
                                      void* (*create)(ComponentStorage&, void*),
                                      void* createArgument,
                                      void (*destroy)(void*)) {
#ifdef FRUIT_EXTRA_DEBUG
  std::cerr << "In ComponentStorage::createTypeInfo for type " << demangleTypeName(typeIndex.name()) << std::endl;
#endif
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");
  auto itr = typeRegistry.find(typeIndex);
  if (itr == typeRegistry.end()) {
    // This type wasn't registered yet, register it.
    TypeInfo& typeInfo = typeRegistry[typeIndex];
    typeInfo.create = create;
    typeInfo.createArgument = createArgument;
    typeInfo.storedSingleton = nullptr;
    typeInfo.destroy = destroy;
  } else {
    // This type was already registered.
    TypeInfo& ourInfo = itr->second;
    check(ourInfo.storedSingleton == nullptr, [=](){ return multipleBindingsError(typeIndex); });
    // At this point it's guaranteed that ourInfo.storedSingleton==nullptr.
    // If the create operations and arguments are equal ok, but if they aren't we
    // can't be sure that they're equivalent and we abort.
    bool equal = ourInfo.create == create
              && ourInfo.createArgument == createArgument;
    check(equal, [=](){ return multipleBindingsError(typeIndex); });
  }
}

void ComponentStorage::createTypeInfo(TypeIndex typeIndex,
                                      void* storedSingleton,
                                      void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");
  auto itr = typeRegistry.find(typeIndex);
  if (itr == typeRegistry.end()) {
    // This type wasn't registered yet, register it.
    TypeInfo& typeInfo = typeRegistry[typeIndex];
    typeInfo.storedSingleton = storedSingleton;
    typeInfo.create = nullptr;
    typeInfo.createArgument = nullptr;
    typeInfo.destroy = destroy;
  } else {
    // This type was already registered.
    TypeInfo& ourInfo = itr->second;
    check(ourInfo.storedSingleton != nullptr, [=](){ return multipleBindingsError(typeIndex); });
    // At this point it's guaranteed that ourInfo.storedSingleton!=nullptr.
    // If the stored singletons and destroy operations are equal ok, but if they aren't we
    // can't be sure that they're equivalent and we abort.
    bool equal = ourInfo.storedSingleton == storedSingleton;
    check(equal, [=](){ return multipleBindingsError(typeIndex); });
  }
}

void ComponentStorage::createTypeInfoForMultibinding(TypeIndex typeIndex,
                                                  void* (*create)(ComponentStorage&, void*),
                                                  void* createArgument,
                                                  void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");

  TypeInfo typeInfo;
  typeInfo.create = create;
  typeInfo.createArgument = createArgument;
  typeInfo.storedSingleton = nullptr;
  typeInfo.destroy = destroy;

  // This works no matter whether typeIndex was registered as a multibinding before or not.
  typeRegistryForMultibindings[typeIndex].insert(typeInfo);
}

void ComponentStorage::createTypeInfoForMultibinding(TypeIndex typeIndex,
                                                  void* storedSingleton,
                                                  void (*destroy)(void*)) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  FruitCheck(parent == nullptr, "Attempting to add a binding after calling setParent().");

  TypeInfo typeInfo;
  typeInfo.storedSingleton = storedSingleton;
  typeInfo.create = nullptr;
  typeInfo.createArgument = nullptr;
  typeInfo.destroy = destroy;

  typeRegistryForMultibindings[typeIndex].insert(typeInfo);
}

void ComponentStorage::setParent(ComponentStorage* parent) {
  FruitCheck(createdSingletons.empty(), "Attempting to add a binding to a component that has already started creating instances");
  this->parent = parent;
  // If the same type is bound in a parent and the child, ensure that the bindings are equivalent and
  // remove the bound in the child.
  for (ComponentStorage* p = parent; p != nullptr; p = p->parent) {
    for (auto i = typeRegistry.begin(), i_end = typeRegistry.end(); i != i_end; /* no increment */) {
      TypeIndex typeIndex = i->first;
      const TypeInfo& childInfo = i->second;
      auto itr = p->typeRegistry.find(typeIndex);
      if (itr == p->typeRegistry.end()) {
        // Type not bound in parent, ok.
        ++i;
        continue;
      }
      const TypeInfo& parentInfo = itr->second;

      // This type was already registered.

      bool equal;
      if (childInfo.storedSingleton != nullptr) {
        // Instance binding.
        equal = childInfo.storedSingleton == parentInfo.storedSingleton;
      } else {
        // Note that parentInfo.storedSingleton may or may not be nullptr.
        equal = childInfo.create == parentInfo.create
            && childInfo.createArgument == parentInfo.createArgument;
      }

      check(equal, [=](){ return multipleBindingsError(typeIndex); });

      i = typeRegistry.erase(i);
    }

    for (auto typeIndexInfoPair : typeRegistryForMultibindings) {
      TypeIndex typeIndex = typeIndexInfoPair.first;
      std::set<TypeInfo>& typeInfos = typeIndexInfoPair.second;
      for (auto i = typeInfos.begin(), i_end = typeInfos.end(); i != i_end; /* no increment */) {
        const TypeInfo& childInfo = *i;
        auto itr = p->typeRegistryForMultibindings.find(typeIndex);
        if (itr == p->typeRegistryForMultibindings.end()) {
          // No multibinding in parent for this type, keep it.
          ++i;
          continue;
        }
        std::set<TypeInfo>& parentInfos = itr->second;
        if (parentInfos.count(childInfo) == 0) {
          // The parent doesn't have the same multibinding, keep it.
          ++i;
          continue;
        }

        // Same multibinding found in parent, remove it from the child.
        i = typeInfos.erase(i);
      }
    }
  }
}

void ComponentStorage::eagerlyInjectMultibindings() {
  for (ComponentStorage* p = this; p != nullptr; p = p->parent) {
    for (auto typeIndexInfoPair : p->typeRegistryForMultibindings) {
      p->ensureConstructedMultibinding(typeIndexInfoPair.first, typeIndexInfoPair.second);
    }
  }
}

} // namespace impl
} // namespace fruit
<|endoftext|>