/**************************************************************
 *
 * 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.
 *
 *************************************************************/



#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:
		// since 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;
}
