/*************************************************************************
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * 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.
 *
************************************************************************/

#include "precompiled_comphelper.hxx"

#include "comphelper_module.hxx"
#include "comphelper/anytostring.hxx"
#include "comphelper/anycompare.hxx"
#include "comphelper/componentbase.hxx"
#include "comphelper/componentcontext.hxx"
#include "comphelper/extract.hxx"

/** === begin UNO includes === **/
#include <com/sun/star/container/XEnumerableMap.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/ucb/AlreadyInitializedException.hpp>
#include <com/sun/star/beans/Pair.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
/** === end UNO includes === **/

#include <cppuhelper/compbase3.hxx>
#include <cppuhelper/implbase1.hxx>
#include <rtl/math.hxx>
#include <rtl/ustrbuf.hxx>
#include <typelib/typedescription.hxx>

#include <map>
#include <boost/shared_ptr.hpp>

//........................................................................
namespace comphelper
{
//........................................................................

	/** === begin UNO using === **/
	using ::com::sun::star::uno::Reference;
	using ::com::sun::star::uno::XInterface;
	using ::com::sun::star::uno::UNO_QUERY;
	using ::com::sun::star::uno::UNO_QUERY_THROW;
	using ::com::sun::star::uno::UNO_SET_THROW;
	using ::com::sun::star::uno::Exception;
	using ::com::sun::star::uno::RuntimeException;
	using ::com::sun::star::uno::Any;
	using ::com::sun::star::uno::makeAny;
	using ::com::sun::star::uno::Sequence;
	using ::com::sun::star::uno::Type;
    using ::com::sun::star::container::XEnumerableMap;
    using ::com::sun::star::lang::NoSupportException;
    using ::com::sun::star::beans::IllegalTypeException;
    using ::com::sun::star::container::NoSuchElementException;
    using ::com::sun::star::lang::IllegalArgumentException;
    using ::com::sun::star::lang::XInitialization;
    using ::com::sun::star::ucb::AlreadyInitializedException;
    using ::com::sun::star::beans::Pair;
    using ::com::sun::star::uno::TypeClass;
    using ::com::sun::star::uno::TypeClass_VOID;
    using ::com::sun::star::uno::TypeClass_UNKNOWN;
    using ::com::sun::star::uno::TypeClass_ANY;
    using ::com::sun::star::uno::TypeClass_EXCEPTION;
    using ::com::sun::star::uno::TypeClass_STRUCT;
    using ::com::sun::star::uno::TypeClass_UNION;
    using ::com::sun::star::uno::TypeClass_FLOAT;
    using ::com::sun::star::uno::TypeClass_DOUBLE;
    using ::com::sun::star::uno::TypeClass_INTERFACE;
    using ::com::sun::star::lang::XServiceInfo;
    using ::com::sun::star::uno::XComponentContext;
    using ::com::sun::star::container::XEnumeration;
    using ::com::sun::star::uno::TypeDescription;
    using ::com::sun::star::lang::WrappedTargetException;
    using ::com::sun::star::lang::DisposedException;
	/** === end UNO using === **/

	//====================================================================
	//= MapData
	//====================================================================
    class IMapModificationListener;
    typedef ::std::vector< IMapModificationListener* > MapListeners;

    typedef ::std::map< Any, Any, LessPredicateAdapter > KeyedValues;
    struct MapData
    {
        Type                                        m_aKeyType;
        Type                                        m_aValueType;
        ::std::auto_ptr< KeyedValues >              m_pValues;
        ::boost::shared_ptr< IKeyPredicateLess >    m_pKeyCompare;
        bool                                        m_bMutable;
        MapListeners                                m_aModListeners;

        MapData()
            :m_bMutable( true )
        {
        }

        MapData( const MapData& _source )
            :m_aKeyType( _source.m_aKeyType )
            ,m_aValueType( _source.m_aValueType )
            ,m_pValues( new KeyedValues( *_source.m_pValues ) )
            ,m_pKeyCompare( _source.m_pKeyCompare )
            ,m_bMutable( false )
            ,m_aModListeners()
        {
        }
    private:
        MapData& operator=( const MapData& _source );   // not implemented
    };

    //====================================================================
	//= IMapModificationListener
	//====================================================================
    /** implemented by components who want to be notified of modifications in the MapData they work with
    */
    class SAL_NO_VTABLE IMapModificationListener
    {
    public:
        /// called when the map was modified
        virtual void mapModified() = 0;
        virtual ~IMapModificationListener()
        {
        }
    };

    //====================================================================
	//= MapData helpers
	//====================================================================
	//--------------------------------------------------------------------
    static void lcl_registerMapModificationListener( MapData& _mapData, IMapModificationListener& _listener )
    {
    #if OSL_DEBUG_LEVEL > 0
        for (   MapListeners::const_iterator lookup = _mapData.m_aModListeners.begin();
                lookup != _mapData.m_aModListeners.end();
                ++lookup
             )
        {
            OSL_ENSURE( *lookup != &_listener, "lcl_registerMapModificationListener: this listener is already registered!" );
        }
    #endif
        _mapData.m_aModListeners.push_back( &_listener );
    }

	//--------------------------------------------------------------------
    static void lcl_revokeMapModificationListener( MapData& _mapData, IMapModificationListener& _listener )
    {
        for (   MapListeners::iterator lookup = _mapData.m_aModListeners.begin();
                lookup != _mapData.m_aModListeners.end();
                ++lookup
             )
        {
            if ( *lookup == &_listener )
            {
                _mapData.m_aModListeners.erase( lookup );
                return;
            }
        }
        OSL_ENSURE( false, "lcl_revokeMapModificationListener: the listener is not registered!" );
    }

	//--------------------------------------------------------------------
    static void lcl_notifyMapDataListeners_nothrow( const MapData& _mapData )
    {
        for (   MapListeners::const_iterator loop = _mapData.m_aModListeners.begin();
                loop != _mapData.m_aModListeners.end();
                ++loop
            )
        {
            (*loop)->mapModified();
        }
    }

	//====================================================================
	//= EnumerableMap
	//====================================================================
    typedef ::cppu::WeakAggComponentImplHelper3 <   XInitialization
                                                ,   XEnumerableMap
                                                ,   XServiceInfo
                                                > Map_IFace;

    class COMPHELPER_DLLPRIVATE EnumerableMap :public Map_IFace
                                    ,public ComponentBase
    {
    protected:
        EnumerableMap( const ComponentContext& _rContext );
        virtual ~EnumerableMap();

        // XInitialization
        virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) throw (Exception, RuntimeException);

        // XEnumerableMap
        virtual ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration > SAL_CALL createKeyEnumeration( ::sal_Bool _Isolated ) throw (::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration > SAL_CALL createValueEnumeration( ::sal_Bool _Isolated ) throw (::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException);
        virtual ::com::sun::star::uno::Reference< ::com::sun::star::container::XEnumeration > SAL_CALL createElementEnumeration( ::sal_Bool _Isolated ) throw (::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException);

        // XMap
        virtual Type SAL_CALL getKeyType() throw (RuntimeException);
        virtual Type SAL_CALL getValueType() throw (RuntimeException);
        virtual void SAL_CALL clear(  ) throw (NoSupportException, RuntimeException);
        virtual ::sal_Bool SAL_CALL containsKey( const Any& _key ) throw (IllegalTypeException, IllegalArgumentException, RuntimeException);
        virtual ::sal_Bool SAL_CALL containsValue( const Any& _value ) throw (IllegalTypeException, IllegalArgumentException, RuntimeException);
        virtual Any SAL_CALL get( const Any& _key ) throw (IllegalTypeException, IllegalArgumentException, NoSuchElementException, RuntimeException);
        virtual Any SAL_CALL put( const Any& _key, const Any& _value ) throw (NoSupportException, IllegalTypeException, IllegalArgumentException, RuntimeException);
        virtual Any SAL_CALL remove( const Any& _key ) throw (NoSupportException, IllegalTypeException, IllegalArgumentException, NoSuchElementException, RuntimeException);

        // XElementAccess (base of XMap)
        virtual Type SAL_CALL getElementType() throw (RuntimeException);
        virtual ::sal_Bool SAL_CALL hasElements() throw (RuntimeException);

        // XServiceInfo
        virtual ::rtl::OUString SAL_CALL getImplementationName(  ) throw (RuntimeException);
        virtual ::sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw (RuntimeException);
        virtual Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames(  ) throw (RuntimeException);

    public:
        // XServiceInfo, static version (used for component registration)
        static ::rtl::OUString SAL_CALL getImplementationName_static(  );
        static Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames_static(  );
        static Reference< XInterface > SAL_CALL Create( const Reference< XComponentContext >& );

    private:
        void    impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues );

        /// throws a IllegalTypeException if the given value is not compatible with our ValueType
        void    impl_checkValue_throw( const Any& _value ) const;
        void    impl_checkKey_throw( const Any& _key ) const;
        void    impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const;
        void    impl_checkMutable_throw() const;

    private:
        ::osl::Mutex        m_aMutex;
        ComponentContext    m_aContext;
        MapData             m_aData;

        ::std::vector< ::com::sun::star::uno::WeakReference< XInterface > >
                            m_aDependentComponents;
    };

	//====================================================================
	//= EnumerationType
	//====================================================================
    enum EnumerationType
    {
        eKeys, eValues, eBoth
    };

	//====================================================================
	//= MapEnumerator
	//====================================================================
    class MapEnumerator : public IMapModificationListener
    {
    public:
        MapEnumerator( ::cppu::OWeakObject& _rParent, MapData& _mapData, const EnumerationType _type )
            :m_rParent( _rParent )
            ,m_rMapData( _mapData )
            ,m_eType( _type )
            ,m_mapPos( _mapData.m_pValues->begin() )
            ,m_disposed( false )
        {
            lcl_registerMapModificationListener( m_rMapData, *this );
        }

        virtual ~MapEnumerator()
        {
            dispose();
        }

        void dispose()
        {
            if ( !m_disposed )
            {
                lcl_revokeMapModificationListener( m_rMapData, *this );
                m_disposed = true;
            }
        }

        // XEnumeration equivalents
        ::sal_Bool hasMoreElements();
        Any nextElement();

        // IMapModificationListener
        virtual void mapModified();

    private:
        ::cppu::OWeakObject&        m_rParent;
        MapData&                    m_rMapData;
        const EnumerationType       m_eType;
        KeyedValues::const_iterator m_mapPos;
        bool                        m_disposed;

    private:
        MapEnumerator();                                    // not implemented
        MapEnumerator( const MapEnumerator& );              // not implemented
        MapEnumerator& operator=( const MapEnumerator& );   // not implemented
    };

	//====================================================================
	//= MapEnumeration
	//====================================================================
    typedef ::cppu::WeakImplHelper1 <   XEnumeration
                                    >   MapEnumeration_Base;
    class MapEnumeration :public ComponentBase
                         ,public MapEnumeration_Base
    {
    public:
        MapEnumeration( ::cppu::OWeakObject& _parentMap, MapData& _mapData, ::cppu::OBroadcastHelper& _rBHelper, 
                        const EnumerationType _type, const bool _isolated )
            :ComponentBase( _rBHelper, ComponentBase::NoInitializationNeeded() )
            ,m_xKeepMapAlive( _parentMap )
            ,m_pMapDataCopy( _isolated ? new MapData( _mapData ) : NULL )
            ,m_aEnumerator( *this, _isolated ? *m_pMapDataCopy : _mapData, _type )
        {
        }

        // XEnumeration
        virtual ::sal_Bool SAL_CALL hasMoreElements(  ) throw (RuntimeException);
        virtual Any SAL_CALL nextElement(  ) throw (NoSuchElementException, WrappedTargetException, RuntimeException);

    protected:
        virtual ~MapEnumeration()
        {
            acquire();
            {
                ::osl::MutexGuard aGuard( getMutex() );
                m_aEnumerator.dispose();
                m_pMapDataCopy.reset();
            }
        }

    private:
        // sicne we share our mutex with the main map, we need to keep it alive as long as we live
        Reference< XInterface >     m_xKeepMapAlive;
        ::std::auto_ptr< MapData >  m_pMapDataCopy;
        MapEnumerator               m_aEnumerator;
    };

	//====================================================================
	//= EnumerableMap
	//====================================================================
	//--------------------------------------------------------------------
    EnumerableMap::EnumerableMap( const ComponentContext& _rContext )
        :Map_IFace( m_aMutex )
        ,ComponentBase( Map_IFace::rBHelper )
        ,m_aContext( _rContext )
    {
    }

	//--------------------------------------------------------------------
    EnumerableMap::~EnumerableMap()
    {
        if ( !impl_isDisposed() )
        {
            acquire();
            dispose();
        }
    }

    //--------------------------------------------------------------------
    void SAL_CALL EnumerableMap::initialize( const Sequence< Any >& _arguments ) throw (Exception, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this, ComponentMethodGuard::WithoutInit );
        if ( impl_isInitialized_nothrow() )
            throw AlreadyInitializedException();

        sal_Int32 nArgumentCount = _arguments.getLength();
        if ( ( nArgumentCount != 2 ) && ( nArgumentCount != 3 ) )
            throw IllegalArgumentException();

        Type aKeyType, aValueType;
        if ( !( _arguments[0] >>= aKeyType ) )
            throw IllegalArgumentException( ::rtl::OUString::createFromAscii( "com.sun.star.uno.Type expected." ), *this, 1 );
        if ( !( _arguments[1] >>= aValueType ) )
            throw IllegalArgumentException( ::rtl::OUString::createFromAscii( "com.sun.star.uno.Type expected." ), *this, 2 );

        Sequence< Pair< Any, Any > > aInitialValues;
        bool bMutable = true;
        if ( nArgumentCount == 3 )
        {
            if ( !( _arguments[2] >>= aInitialValues ) )
                throw IllegalArgumentException( ::rtl::OUString::createFromAscii( "[]com.sun.star.beans.Pair<any,any> expected." ), *this, 2 );
            bMutable = false;
        }

        // for the value, anything is allowed, except VOID
        if ( ( aValueType.getTypeClass() == TypeClass_VOID ) || ( aValueType.getTypeClass() == TypeClass_UNKNOWN ) )
            throw IllegalTypeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unsupported value type." ) ), *this );

        // create the comparator for the KeyType, and throw if the type is not supported
        ::std::auto_ptr< IKeyPredicateLess > pComparator( getStandardLessPredicate( aKeyType, NULL ) );
        if ( !pComparator.get() )
            throw IllegalTypeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Unsupported key type." ) ), *this );

        // init members
        m_aData.m_aKeyType = aKeyType;
        m_aData.m_aValueType = aValueType;
        m_aData.m_pKeyCompare = pComparator;
        m_aData.m_pValues.reset( new KeyedValues( *m_aData.m_pKeyCompare ) );
        m_aData.m_bMutable = bMutable;

        if ( aInitialValues.getLength() )
            impl_initValues_throw( aInitialValues );

        setInitialized();
    }

    //--------------------------------------------------------------------
    void EnumerableMap::impl_initValues_throw( const Sequence< Pair< Any, Any > >& _initialValues )
    {
        OSL_PRECOND( m_aData.m_pValues.get() && m_aData.m_pValues->empty(), "EnumerableMap::impl_initValues_throw: illegal call!" );
        if ( !m_aData.m_pValues.get() || !m_aData.m_pValues->empty() )
            throw RuntimeException();

        const Pair< Any, Any >* mapping = _initialValues.getConstArray();
        const Pair< Any, Any >* mappingEnd = mapping + _initialValues.getLength();
        Any normalizedValue;
        for ( ; mapping != mappingEnd; ++mapping )
        {
            impl_checkValue_throw( mapping->Second );
            (*m_aData.m_pValues)[ mapping->First ] = mapping->Second;
        }
    }

    //--------------------------------------------------------------------
    void EnumerableMap::impl_checkValue_throw( const Any& _value ) const
    {
        if ( !_value.hasValue() )
            // nothing to do, NULL values are always allowed, regardless of the ValueType
            return;

        TypeClass eAllowedTypeClass = m_aData.m_aValueType.getTypeClass();
        bool bValid = false;

        switch ( eAllowedTypeClass )
        {
        default:
            bValid = ( _value.getValueTypeClass() == eAllowedTypeClass );
            break;
        case TypeClass_ANY:
            bValid = true;
            break;
        case TypeClass_INTERFACE:
        {
            // special treatment: _value might contain the proper type, but the interface
            // might actually be NULL. Which is still valid ...
            if ( m_aData.m_aValueType.isAssignableFrom( _value.getValueType() ) )
                // this also catches the special case where XFoo is our value type,
                // and _value contains a NULL-reference to XFoo, or a derived type
                bValid = true;
            else
            {
                Reference< XInterface > xValue( _value, UNO_QUERY );
                Any aTypedValue;
                if ( xValue.is() )
                    // XInterface is not-NULL, but is X(ValueType) not-NULL, too?
                    xValue.set( xValue->queryInterface( m_aData.m_aValueType ), UNO_QUERY );
                bValid = xValue.is();
            }
        }
        break;
        case TypeClass_EXCEPTION:
        case TypeClass_STRUCT:
        case TypeClass_UNION:
        {
            // values are accepted if and only if their type equals, or is derived from, our value type

            if ( _value.getValueTypeClass() != eAllowedTypeClass )
                bValid = false;
            else
            {
                const TypeDescription aValueTypeDesc( _value.getValueType() );
                const TypeDescription aRequiredTypeDesc( m_aData.m_aValueType );

                const _typelib_CompoundTypeDescription* pValueCompoundTypeDesc =
                    reinterpret_cast< const _typelib_CompoundTypeDescription* >( aValueTypeDesc.get() );

                while ( pValueCompoundTypeDesc )
                {
                    if ( typelib_typedescription_equals( &pValueCompoundTypeDesc->aBase, aRequiredTypeDesc.get() ) )
                        break;
                    pValueCompoundTypeDesc = pValueCompoundTypeDesc->pBaseTypeDescription;
                }
                bValid = ( pValueCompoundTypeDesc != NULL );
            }
        }
        break;
        }

        if ( !bValid )
        {
            ::rtl::OUStringBuffer aMessage;
            aMessage.appendAscii( "Incompatible value type. Found '" );
            aMessage.append( _value.getValueTypeName() );
            aMessage.appendAscii( "', where '" );
            aMessage.append( m_aData.m_aValueType.getTypeName() );
            aMessage.appendAscii( "' (or compatible type) is expected." );
            throw IllegalTypeException( aMessage.makeStringAndClear(), *const_cast< EnumerableMap* >( this ) );
        }

        impl_checkNaN_throw( _value, m_aData.m_aValueType );
    }

    //--------------------------------------------------------------------
    void EnumerableMap::impl_checkNaN_throw( const Any& _keyOrValue, const Type& _keyOrValueType ) const
    {
        if  (   ( _keyOrValueType.getTypeClass() == TypeClass_DOUBLE )
            ||  ( _keyOrValueType.getTypeClass() == TypeClass_FLOAT )
            )
        {
            double nValue(0);
            if ( _keyOrValue >>= nValue )
                if ( ::rtl::math::isNan( nValue ) )
                    throw IllegalArgumentException(
                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NaN (not-a-number) not supported by this implementation." ) ),
                        *const_cast< EnumerableMap* >( this ), 0 );
            // (note that the case of _key not containing a float/double value is handled in the
            // respective IKeyPredicateLess implementation, so there's no need to handle this here.)
        }
    }

    //--------------------------------------------------------------------
    void EnumerableMap::impl_checkKey_throw( const Any& _key ) const
    {
        if ( !_key.hasValue() )
            throw IllegalArgumentException(
                ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NULL keys not supported by this implementation." ) ),
                *const_cast< EnumerableMap* >( this ), 0 );

        impl_checkNaN_throw( _key, m_aData.m_aKeyType );
    }

    //--------------------------------------------------------------------
    void EnumerableMap::impl_checkMutable_throw() const
    {
        if ( !m_aData.m_bMutable )
            throw NoSupportException(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "The map is immutable." ) ),
                    *const_cast< EnumerableMap* >( this ) );
    }

    //--------------------------------------------------------------------
    Reference< XEnumeration > SAL_CALL EnumerableMap::createKeyEnumeration( ::sal_Bool _Isolated ) throw (NoSupportException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eKeys, _Isolated );
    }
    
    //--------------------------------------------------------------------
    Reference< XEnumeration > SAL_CALL EnumerableMap::createValueEnumeration( ::sal_Bool _Isolated ) throw (NoSupportException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eValues, _Isolated );
    }
    
    //--------------------------------------------------------------------
    Reference< XEnumeration > SAL_CALL EnumerableMap::createElementEnumeration( ::sal_Bool _Isolated ) throw (NoSupportException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return new MapEnumeration( *this, m_aData, getBroadcastHelper(), eBoth, _Isolated );
    }

    //--------------------------------------------------------------------
    Type SAL_CALL EnumerableMap::getKeyType() throw (RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return m_aData.m_aKeyType;
    }
    
    //--------------------------------------------------------------------
    Type SAL_CALL EnumerableMap::getValueType() throw (RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return m_aData.m_aValueType;
    }
    
    //--------------------------------------------------------------------
    void SAL_CALL EnumerableMap::clear(  ) throw (NoSupportException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkMutable_throw();

        m_aData.m_pValues->clear();

        lcl_notifyMapDataListeners_nothrow( m_aData );
    }
    
    //--------------------------------------------------------------------
    ::sal_Bool SAL_CALL EnumerableMap::containsKey( const Any& _key ) throw (IllegalTypeException, IllegalArgumentException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkKey_throw( _key );

        KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
        return ( pos != m_aData.m_pValues->end() );
    }
    
    //--------------------------------------------------------------------
    ::sal_Bool SAL_CALL EnumerableMap::containsValue( const Any& _value ) throw (IllegalTypeException, IllegalArgumentException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkValue_throw( _value );

        for (   KeyedValues::const_iterator mapping = m_aData.m_pValues->begin();
                mapping != m_aData.m_pValues->end();
                ++mapping
            )
        {
            if ( mapping->second == _value )
                return sal_True;
        }
        return sal_False;
    }
    
    //--------------------------------------------------------------------
    Any SAL_CALL EnumerableMap::get( const Any& _key ) throw (IllegalTypeException, IllegalArgumentException, NoSuchElementException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkKey_throw( _key );

        KeyedValues::const_iterator pos = m_aData.m_pValues->find( _key );
        if ( pos == m_aData.m_pValues->end() )
            throw NoSuchElementException( anyToString( _key ), *this );

        return pos->second;
    }
    
    //--------------------------------------------------------------------
    Any SAL_CALL EnumerableMap::put( const Any& _key, const Any& _value ) throw (NoSupportException, IllegalTypeException, IllegalArgumentException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkMutable_throw();
        impl_checkKey_throw( _key );
        impl_checkValue_throw( _value );

        Any previousValue;

        KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
        if ( pos != m_aData.m_pValues->end() )
        {
            previousValue = pos->second;
            pos->second = _value;
        }
        else
        {
            (*m_aData.m_pValues)[ _key ] = _value;
        }

        lcl_notifyMapDataListeners_nothrow( m_aData );

        return previousValue;
    }
    
    //--------------------------------------------------------------------
    Any SAL_CALL EnumerableMap::remove( const Any& _key ) throw (NoSupportException, IllegalTypeException, IllegalArgumentException, NoSuchElementException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        impl_checkMutable_throw();
        impl_checkKey_throw( _key );

        Any previousValue;

        KeyedValues::iterator pos = m_aData.m_pValues->find( _key );
        if ( pos != m_aData.m_pValues->end() )
        {
            previousValue = pos->second;
            m_aData.m_pValues->erase( pos );
        }

        lcl_notifyMapDataListeners_nothrow( m_aData );

        return previousValue;
    }

    //--------------------------------------------------------------------
    Type SAL_CALL EnumerableMap::getElementType() throw (RuntimeException)
    {
        return ::cppu::UnoType< Pair< Any, Any > >::get();
    }

    //--------------------------------------------------------------------
    ::sal_Bool SAL_CALL EnumerableMap::hasElements() throw (RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return m_aData.m_pValues->empty();
    }

    //--------------------------------------------------------------------
    ::rtl::OUString SAL_CALL EnumerableMap::getImplementationName(  ) throw (RuntimeException)
    {
        return getImplementationName_static();
    }

    //--------------------------------------------------------------------
    ::sal_Bool SAL_CALL EnumerableMap::supportsService( const ::rtl::OUString& _serviceName ) throw (RuntimeException)
    {
        Sequence< ::rtl::OUString > aServices( getSupportedServiceNames() );
        for ( sal_Int32 i=0; i<aServices.getLength(); ++i )
            if ( _serviceName == aServices[i] )
                return sal_True;
        return sal_False;
    }

    //--------------------------------------------------------------------
    Sequence< ::rtl::OUString > SAL_CALL EnumerableMap::getSupportedServiceNames(  ) throw (RuntimeException)
    {
        return getSupportedServiceNames_static();
    }

    //--------------------------------------------------------------------
    ::rtl::OUString SAL_CALL EnumerableMap::getImplementationName_static(  )
    {
        return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.comp.comphelper.EnumerableMap" ) );
    }

    //--------------------------------------------------------------------
    Sequence< ::rtl::OUString > SAL_CALL EnumerableMap::getSupportedServiceNames_static(  )
    {
        Sequence< ::rtl::OUString > aServiceNames(1);
        aServiceNames[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.container.EnumerableMap" ) );
        return aServiceNames;
    }

    //--------------------------------------------------------------------
    Reference< XInterface > SAL_CALL EnumerableMap::Create( const Reference< XComponentContext >& _context )
    {
        return *new EnumerableMap( ComponentContext( _context ) );
    }

	//====================================================================
	//= MapEnumerator
	//====================================================================
    //--------------------------------------------------------------------
    ::sal_Bool MapEnumerator::hasMoreElements()
    {
        if ( m_disposed )
            throw DisposedException( ::rtl::OUString(), m_rParent );
        return m_mapPos != m_rMapData.m_pValues->end();
    }

    //--------------------------------------------------------------------
    Any MapEnumerator::nextElement()
    {
        if ( m_disposed )
            throw DisposedException( ::rtl::OUString(), m_rParent );
        if ( m_mapPos == m_rMapData.m_pValues->end() )
            throw NoSuchElementException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No more elements." ) ), m_rParent );

        Any aNextElement;
        switch ( m_eType )
        {
        case eKeys:     aNextElement = m_mapPos->first; break;
        case eValues:   aNextElement = m_mapPos->second; break;
        case eBoth:     aNextElement <<= Pair< Any, Any >( m_mapPos->first, m_mapPos->second ); break;
        }
        ++m_mapPos;
        return aNextElement;
    }

    //--------------------------------------------------------------------
    void MapEnumerator::mapModified()
    {
        m_disposed = true;
    }

	//====================================================================
	//= MapEnumeration - implementation
	//====================================================================
    //--------------------------------------------------------------------
    ::sal_Bool SAL_CALL MapEnumeration::hasMoreElements(  ) throw (RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return m_aEnumerator.hasMoreElements();
    }
    
    //--------------------------------------------------------------------
    Any SAL_CALL MapEnumeration::nextElement(  ) throw (NoSuchElementException, WrappedTargetException, RuntimeException)
    {
        ComponentMethodGuard aGuard( *this );
        return m_aEnumerator.nextElement();
    }

//........................................................................
} // namespace comphelper
//........................................................................

void createRegistryInfo_Map()
{
    ::comphelper::module::OAutoRegistration< ::comphelper::EnumerableMap > aAutoRegistration;
}
