/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_cppuhelper.hxx"
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/compbase.hxx>
#include <osl/diagnose.h>
#include <rtl/uuid.h>

#include <com/sun/star/lang/XComponent.hpp>
#include "com/sun/star/uno/RuntimeException.hpp"

using namespace ::osl;
using namespace ::rtl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace cppu
{
//==================================================================================================
Mutex & SAL_CALL getImplHelperInitMutex(void) SAL_THROW( () )
{
	static Mutex * s_pMutex = 0;
	if (! s_pMutex)
	{
		MutexGuard aGuard( Mutex::getGlobalMutex() );
		if (! s_pMutex)
		{
			static Mutex s_aMutex;
			s_pMutex = & s_aMutex;
		}
	}
	return * s_pMutex;
}

// ClassDataBase
//__________________________________________________________________________________________________
ClassDataBase::ClassDataBase() SAL_THROW( () )
	: bOffsetsInit( sal_False )
	, nType2Offset( 0 )
	, nClassCode( 0 )
	, pTypes( 0 )
	, pId( 0 )
{
}
//__________________________________________________________________________________________________
ClassDataBase::ClassDataBase( sal_Int32 nClassCode_ ) SAL_THROW( () )
	: bOffsetsInit( sal_False )
	, nType2Offset( 0 )
	, nClassCode( nClassCode_ )
	, pTypes( 0 )
	, pId( 0 )
{
}
//__________________________________________________________________________________________________
ClassDataBase::~ClassDataBase() SAL_THROW( () )
{
	delete pTypes;
	delete pId;

	for ( sal_Int32 nPos = nType2Offset; nPos--; )
	{
		typelib_typedescription_release(
			(typelib_TypeDescription *)((ClassData *)this)->arType2Offset[nPos].pTD );
	}
}

// ClassData
//__________________________________________________________________________________________________
void ClassData::writeTypeOffset( const Type & rType, sal_Int32 nOffset ) SAL_THROW( () )
{
	arType2Offset[nType2Offset].nOffset = nOffset;

	arType2Offset[nType2Offset].pTD = 0;
	typelib_typedescriptionreference_getDescription(
		(typelib_TypeDescription **)&arType2Offset[nType2Offset].pTD, rType.getTypeLibType() );

	if (arType2Offset[nType2Offset].pTD)
		++nType2Offset;
#if OSL_DEBUG_LEVEL > 1
	else
	{
		OString msg( "### cannot get type description for " );
		msg += OUStringToOString( rType.getTypeName(), RTL_TEXTENCODING_ASCII_US );
		OSL_ENSURE( sal_False, msg.getStr() );
	}
#endif
}
//__________________________________________________________________________________________________
void ClassData::initTypeProvider() SAL_THROW( () )
{
	::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
	if (! pTypes)
	{
		// create id
		pId = new Sequence< sal_Int8 >( 16 );
		rtl_createUuid( (sal_uInt8 *)pId->getArray(), 0, sal_True );

		// collect types
		Sequence< Type > * types = new Sequence< Type >(
			nType2Offset + 1 + (nClassCode == 4 ? 2 : nClassCode) );
		Type * pTypeAr = types->getArray();

		// given types
		sal_Int32 nPos = nType2Offset;
		while (nPos--)
			pTypeAr[nPos] = ((typelib_TypeDescription *)arType2Offset[nPos].pTD)->pWeakRef;

		// XTypeProvider
		pTypeAr[nType2Offset] = ::getCppuType( (const Reference< lang::XTypeProvider > *)0 );

		// class code extra types: [[XComponent,] XWeak[, XAggregation]]
		switch (nClassCode)
		{
		case 4:
			pTypeAr[nType2Offset +2] = ::getCppuType( (const Reference< lang::XComponent > *)0 );
			pTypeAr[nType2Offset +1] = ::getCppuType( (const Reference< XWeak > *)0 );
			break;
		case 3:
			pTypeAr[nType2Offset +3] = ::getCppuType( (const Reference< lang::XComponent > *)0 );
		case 2:
			pTypeAr[nType2Offset +2] = ::getCppuType( (const Reference< XAggregation > *)0 );
		case 1:
			pTypeAr[nType2Offset +1] = ::getCppuType( (const Reference< XWeak > *)0 );
		}

		pTypes = types;
	}
}
//__________________________________________________________________________________________________
Sequence< Type > ClassData::getTypes() SAL_THROW( () )
{
	if (! pTypes)
		initTypeProvider();
	return *pTypes;
}
//__________________________________________________________________________________________________
Sequence< sal_Int8 > ClassData::getImplementationId() SAL_THROW( () )
{
	if (! pTypes)
		initTypeProvider();
	return *pId;
}

//--------------------------------------------------------------------------------------------------
static inline sal_Bool td_equals(
	typelib_TypeDescription * pTD, typelib_TypeDescriptionReference * pType )
	SAL_THROW( () )
{
	return (pTD->pWeakRef == pType ||
			(pTD->pTypeName->length == pType->pTypeName->length &&
			 rtl_ustr_compare( pTD->pTypeName->buffer, pType->pTypeName->buffer ) == 0));
}
//__________________________________________________________________________________________________
Any ClassData::query( const Type & rType, lang::XTypeProvider * pBase ) SAL_THROW( () )
{
	if (rType == ::getCppuType( (const Reference< XInterface > *)0 ))
		return Any( &pBase, ::getCppuType( (const Reference< XInterface > *)0 ) );
	for ( sal_Int32 nPos = 0; nPos < nType2Offset; ++nPos )
	{
		const Type_Offset & rTO = arType2Offset[nPos];
		typelib_InterfaceTypeDescription * pTD = rTO.pTD;
		while (pTD)
		{
			if (td_equals( (typelib_TypeDescription *)pTD,
						   *(typelib_TypeDescriptionReference **)&rType ))
			{
				void * pInterface = (char *)pBase + rTO.nOffset;
				return Any( &pInterface, (typelib_TypeDescription *)pTD );
			}
			pTD = pTD->pBaseTypeDescription;
		}
	}
	if (rType == ::getCppuType( (const Reference< lang::XTypeProvider > *)0 ))
		return Any( &pBase, ::getCppuType( (const Reference< lang::XTypeProvider > *)0 ) );

	return Any();
}

//##################################################################################################
//##################################################################################################
//##################################################################################################

// WeakComponentImplHelperBase
//__________________________________________________________________________________________________
WeakComponentImplHelperBase::WeakComponentImplHelperBase( Mutex & rMutex )
    SAL_THROW( () )
    : rBHelper( rMutex )
{
}
//__________________________________________________________________________________________________
WeakComponentImplHelperBase::~WeakComponentImplHelperBase()
    SAL_THROW( () )
{
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::disposing()
{
}
//__________________________________________________________________________________________________
Any WeakComponentImplHelperBase::queryInterface( Type const & rType )
    throw (RuntimeException)
{
    if (rType == ::getCppuType( (Reference< lang::XComponent > const *)0 ))
    {
        void * p = static_cast< lang::XComponent * >( this );
        return Any( &p, rType );
    }
    return OWeakObject::queryInterface( rType );
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::acquire()
    throw ()
{
    OWeakObject::acquire();
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::release()
    throw ()
{
    if (osl_decrementInterlockedCount( &m_refCount ) == 0) {
        // ensure no other references are created, via the weak connection point, from now on
        disposeWeakConnectionPoint();
        // restore reference count:
        osl_incrementInterlockedCount( &m_refCount );
        if (! rBHelper.bDisposed) {
            try {
                dispose();
            }
            catch (RuntimeException const& exc) { // don't break throw ()
                OSL_ENSURE(
                    false, OUStringToOString(
                        exc.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
                static_cast<void>(exc);
            }
            OSL_ASSERT( rBHelper.bDisposed );
        }
        OWeakObject::release();
    }
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::dispose()
    throw (RuntimeException)
{
    ClearableMutexGuard aGuard( rBHelper.rMutex );
    if (!rBHelper.bDisposed && !rBHelper.bInDispose)
    {
        rBHelper.bInDispose = sal_True;
        aGuard.clear();
        try
        {
            // side effect: keeping a reference to this
            lang::EventObject aEvt( static_cast< OWeakObject * >( this ) );
            try
            {
                rBHelper.aLC.disposeAndClear( aEvt );
                disposing();
            }
            catch (...)
            {
                MutexGuard aGuard2( rBHelper.rMutex );
                // bDisposed and bInDispose must be set in this order:
                rBHelper.bDisposed = sal_True;
                rBHelper.bInDispose = sal_False;
                throw;
            }
            MutexGuard aGuard2( rBHelper.rMutex );
            // bDisposed and bInDispose must be set in this order:
            rBHelper.bDisposed = sal_True;
            rBHelper.bInDispose = sal_False;
        }
        catch (RuntimeException &)
        {
            throw;
        }
        catch (Exception & exc)
        {
            throw RuntimeException(
                OUString( RTL_CONSTASCII_USTRINGPARAM(
                              "unexpected UNO exception caught: ") ) +
                exc.Message, Reference< XInterface >() );
        }
    }
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::addEventListener(
    Reference< lang::XEventListener > const & xListener )
    throw (RuntimeException)
{
    ClearableMutexGuard aGuard( rBHelper.rMutex );
	if (rBHelper.bDisposed || rBHelper.bInDispose)
	{
        aGuard.clear();
        lang::EventObject aEvt( static_cast< OWeakObject * >( this ) );
		xListener->disposing( aEvt );
	}
    else
    {
        rBHelper.addListener( ::getCppuType( &xListener ), xListener );
    }
}
//__________________________________________________________________________________________________
void WeakComponentImplHelperBase::removeEventListener(
    Reference< lang::XEventListener > const & xListener )
    throw (RuntimeException)
{
    rBHelper.removeListener( ::getCppuType( &xListener ), xListener );
}

// WeakAggComponentImplHelperBase
//__________________________________________________________________________________________________
WeakAggComponentImplHelperBase::WeakAggComponentImplHelperBase( Mutex & rMutex )
    SAL_THROW( () )
    : rBHelper( rMutex )
{
}
//__________________________________________________________________________________________________
WeakAggComponentImplHelperBase::~WeakAggComponentImplHelperBase()
    SAL_THROW( () )
{
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::disposing()
{
}
//__________________________________________________________________________________________________
Any WeakAggComponentImplHelperBase::queryInterface( Type const & rType )
    throw (RuntimeException)
{
    return OWeakAggObject::queryInterface( rType );
}
//__________________________________________________________________________________________________
Any WeakAggComponentImplHelperBase::queryAggregation( Type const & rType )
    throw (RuntimeException)
{
    if (rType == ::getCppuType( (Reference< lang::XComponent > const *)0 ))
    {
        void * p = static_cast< lang::XComponent * >( this );
        return Any( &p, rType );
    }
    return OWeakAggObject::queryAggregation( rType );
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::acquire()
    throw ()
{
    OWeakAggObject::acquire();
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::release()
    throw ()
{
    Reference<XInterface> const xDelegator_(xDelegator);
    if (xDelegator_.is()) {
        OWeakAggObject::release();
    }
    else if (osl_decrementInterlockedCount( &m_refCount ) == 0) {
        // ensure no other references are created, via the weak connection point, from now on
        disposeWeakConnectionPoint();
        // restore reference count:
        osl_incrementInterlockedCount( &m_refCount );
        if (! rBHelper.bDisposed) {
            try {
                dispose();
            }
            catch (RuntimeException const& exc) { // don't break throw ()
                OSL_ENSURE(
                    false, OUStringToOString(
                        exc.Message, RTL_TEXTENCODING_ASCII_US ).getStr() );
                static_cast<void>(exc);
            }
            OSL_ASSERT( rBHelper.bDisposed );
        }
        OWeakAggObject::release();
    }
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::dispose()
    throw (RuntimeException)
{
    ClearableMutexGuard aGuard( rBHelper.rMutex );
    if (!rBHelper.bDisposed && !rBHelper.bInDispose)
    {
        rBHelper.bInDispose = sal_True;
        aGuard.clear();
        try
        {
            // side effect: keeping a reference to this
            lang::EventObject aEvt( static_cast< OWeakObject * >( this ) );
            try
            {
                rBHelper.aLC.disposeAndClear( aEvt );
                disposing();
            }
            catch (...)
            {
                MutexGuard aGuard2( rBHelper.rMutex );
                // bDisposed and bInDispose must be set in this order:
                rBHelper.bDisposed = sal_True;
                rBHelper.bInDispose = sal_False;
                throw;
            }
            MutexGuard aGuard2( rBHelper.rMutex );
            // bDisposed and bInDispose must be set in this order:
            rBHelper.bDisposed = sal_True;
            rBHelper.bInDispose = sal_False;
        }
        catch (RuntimeException &)
        {
            throw;
        }
        catch (Exception & exc)
        {
            throw RuntimeException(
                OUString( RTL_CONSTASCII_USTRINGPARAM(
                              "unexpected UNO exception caught: ") ) +
                exc.Message, Reference< XInterface >() );
        }
    }
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::addEventListener(
    Reference< lang::XEventListener > const & xListener )
    throw (RuntimeException)
{
    ClearableMutexGuard aGuard( rBHelper.rMutex );
	if (rBHelper.bDisposed || rBHelper.bInDispose)
	{
        aGuard.clear();
        lang::EventObject aEvt( static_cast< OWeakObject * >( this ) );
		xListener->disposing( aEvt );
	}
    else
    {
        rBHelper.addListener( ::getCppuType( &xListener ), xListener );
    }
}
//__________________________________________________________________________________________________
void WeakAggComponentImplHelperBase::removeEventListener(
    Reference< lang::XEventListener > const & xListener )
    throw (RuntimeException)
{
    rBHelper.removeListener( ::getCppuType( &xListener ), xListener );
}

}
