1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <comphelper/anytostring.hxx>
22 #include <comphelper/anycompare.hxx>
23 #include <comphelper/componentbase.hxx>
25 #include <com/sun/star/container/XEnumerableMap.hpp>
26 #include <com/sun/star/lang/NoSupportException.hpp>
27 #include <com/sun/star/lang/XInitialization.hpp>
28 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
29 #include <com/sun/star/beans/IllegalTypeException.hpp>
30 #include <com/sun/star/beans/Pair.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/uno/XComponentContext.hpp>
34 #include <cppuhelper/compbase3.hxx>
35 #include <cppuhelper/implbase.hxx>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <typelib/typedescription.hxx>
49 using ::com::sun::star::uno::Reference
;
50 using ::com::sun::star::uno::XInterface
;
51 using ::com::sun::star::uno::UNO_QUERY
;
52 using ::com::sun::star::uno::RuntimeException
;
53 using ::com::sun::star::uno::Any
;
54 using ::com::sun::star::uno::Sequence
;
55 using ::com::sun::star::uno::Type
;
56 using ::com::sun::star::container::XEnumerableMap
;
57 using ::com::sun::star::lang::NoSupportException
;
58 using ::com::sun::star::beans::IllegalTypeException
;
59 using ::com::sun::star::container::NoSuchElementException
;
60 using ::com::sun::star::lang::IllegalArgumentException
;
61 using ::com::sun::star::lang::XInitialization
;
62 using ::com::sun::star::ucb::AlreadyInitializedException
;
63 using ::com::sun::star::beans::Pair
;
64 using ::com::sun::star::uno::TypeClass
;
65 using ::com::sun::star::uno::TypeClass_VOID
;
66 using ::com::sun::star::uno::TypeClass_UNKNOWN
;
67 using ::com::sun::star::uno::TypeClass_ANY
;
68 using ::com::sun::star::uno::TypeClass_EXCEPTION
;
69 using ::com::sun::star::uno::TypeClass_STRUCT
;
70 using ::com::sun::star::uno::TypeClass_FLOAT
;
71 using ::com::sun::star::uno::TypeClass_DOUBLE
;
72 using ::com::sun::star::uno::TypeClass_INTERFACE
;
73 using ::com::sun::star::lang::XServiceInfo
;
74 using ::com::sun::star::uno::XComponentContext
;
75 using ::com::sun::star::container::XEnumeration
;
76 using ::com::sun::star::uno::TypeDescription
;
77 using ::com::sun::star::lang::DisposedException
;
85 typedef std::map
< Any
, Any
, LessPredicateAdapter
> KeyedValues
;
93 std::optional
< KeyedValues
> m_pValues
;
94 std::shared_ptr
< IKeyPredicateLess
> m_pKeyCompare
;
96 std::vector
< MapEnumerator
* > m_aModListeners
;
103 MapData( const MapData
& _source
)
104 :m_aKeyType( _source
.m_aKeyType
)
105 ,m_aValueType( _source
.m_aValueType
)
106 ,m_pKeyCompare( _source
.m_pKeyCompare
)
109 m_pValues
.emplace( *_source
.m_pValues
);
112 MapData
& operator=( const MapData
& _source
) = delete;
117 static void lcl_registerMapModificationListener( MapData
& _mapData
, MapEnumerator
& _listener
)
119 #if OSL_DEBUG_LEVEL > 0
120 for ( const MapEnumerator
* lookup
: _mapData
.m_aModListeners
)
122 OSL_ENSURE( lookup
!= &_listener
, "lcl_registerMapModificationListener: this listener is already registered!" );
125 _mapData
.m_aModListeners
.push_back( &_listener
);
129 static void lcl_revokeMapModificationListener( MapData
& _mapData
, MapEnumerator
& _listener
)
131 auto lookup
= std::find(_mapData
.m_aModListeners
.begin(), _mapData
.m_aModListeners
.end(), &_listener
);
132 if (lookup
!= _mapData
.m_aModListeners
.end())
134 _mapData
.m_aModListeners
.erase( lookup
);
137 OSL_FAIL( "lcl_revokeMapModificationListener: the listener is not registered!" );
141 static void lcl_notifyMapDataListeners_nothrow( const MapData
& _mapData
);
146 typedef ::cppu::WeakAggComponentImplHelper3
< XInitialization
153 class EnumerableMap
: public Map_IFace
, public ComponentBase
158 virtual ~EnumerableMap() override
;
161 virtual void SAL_CALL
initialize( const Sequence
< Any
>& aArguments
) override
;
164 virtual css::uno::Reference
< css::container::XEnumeration
> SAL_CALL
createKeyEnumeration( sal_Bool Isolated
) override
;
165 virtual css::uno::Reference
< css::container::XEnumeration
> SAL_CALL
createValueEnumeration( sal_Bool Isolated
) override
;
166 virtual css::uno::Reference
< css::container::XEnumeration
> SAL_CALL
createElementEnumeration( sal_Bool Isolated
) override
;
169 virtual Type SAL_CALL
getKeyType() override
;
170 virtual Type SAL_CALL
getValueType() override
;
171 virtual void SAL_CALL
clear( ) override
;
172 virtual sal_Bool SAL_CALL
containsKey( const Any
& _key
) override
;
173 virtual sal_Bool SAL_CALL
containsValue( const Any
& _value
) override
;
174 virtual Any SAL_CALL
get( const Any
& _key
) override
;
175 virtual Any SAL_CALL
put( const Any
& _key
, const Any
& _value
) override
;
176 virtual Any SAL_CALL
remove( const Any
& _key
) override
;
178 // XElementAccess (base of XMap)
179 virtual Type SAL_CALL
getElementType() override
;
180 virtual sal_Bool SAL_CALL
hasElements() override
;
183 virtual OUString SAL_CALL
getImplementationName( ) override
;
184 virtual sal_Bool SAL_CALL
supportsService( const OUString
& ServiceName
) override
;
185 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames( ) override
;
188 void impl_initValues_throw( const Sequence
< Pair
< Any
, Any
> >& _initialValues
);
190 /// throws an IllegalTypeException if the given value is not compatible with our ValueType
191 void impl_checkValue_throw( const Any
& _value
) const;
192 void impl_checkKey_throw( const Any
& _key
) const;
193 void impl_checkNaN_throw( const Any
& _keyOrValue
, const Type
& _keyOrValueType
) const;
194 void impl_checkMutable_throw() const;
197 ::osl::Mutex m_aMutex
;
203 eKeys
, eValues
, eBoth
206 class MapEnumerator final
209 MapEnumerator( ::cppu::OWeakObject
& _rParent
, MapData
& _mapData
, const EnumerationType _type
)
210 :m_rParent( _rParent
)
211 ,m_rMapData( _mapData
)
213 ,m_mapPos( _mapData
.m_pValues
->begin() )
216 lcl_registerMapModificationListener( m_rMapData
, *this );
228 lcl_revokeMapModificationListener( m_rMapData
, *this );
234 MapEnumerator(const MapEnumerator
&) = delete;
235 const MapEnumerator
& operator=(const MapEnumerator
&) = delete;
237 // XEnumeration equivalents
238 bool hasMoreElements();
241 /// called when the map was modified
245 ::cppu::OWeakObject
& m_rParent
;
247 const EnumerationType m_eType
;
248 KeyedValues::const_iterator m_mapPos
;
254 static void lcl_notifyMapDataListeners_nothrow( const MapData
& _mapData
)
256 for ( MapEnumerator
* loop
: _mapData
.m_aModListeners
)
262 typedef ::cppu::WeakImplHelper
< XEnumeration
263 > MapEnumeration_Base
;
267 class MapEnumeration
:public ComponentBase
268 ,public MapEnumeration_Base
271 MapEnumeration( ::cppu::OWeakObject
& _parentMap
, MapData
& _mapData
, ::cppu::OBroadcastHelper
& _rBHelper
,
272 const EnumerationType _type
, const bool _isolated
)
273 :ComponentBase( _rBHelper
, ComponentBase::NoInitializationNeeded() )
274 ,m_xKeepMapAlive( _parentMap
)
275 ,m_pMapDataCopy( _isolated
? new MapData( _mapData
) : nullptr )
276 ,m_aEnumerator( *this, _isolated
? *m_pMapDataCopy
: _mapData
, _type
)
281 virtual sal_Bool SAL_CALL
hasMoreElements( ) override
;
282 virtual Any SAL_CALL
nextElement( ) override
;
285 virtual ~MapEnumeration() override
289 ::osl::MutexGuard
aGuard( getMutex() );
290 m_aEnumerator
.dispose();
291 m_pMapDataCopy
.reset();
296 // since we share our mutex with the main map, we need to keep it alive as long as we live
297 Reference
< XInterface
> m_xKeepMapAlive
;
298 std::unique_ptr
< MapData
> m_pMapDataCopy
;
299 MapEnumerator m_aEnumerator
;
304 EnumerableMap::EnumerableMap()
305 :Map_IFace( m_aMutex
)
306 ,ComponentBase( Map_IFace::rBHelper
)
311 EnumerableMap::~EnumerableMap()
313 if ( !impl_isDisposed() )
321 void SAL_CALL
EnumerableMap::initialize( const Sequence
< Any
>& _arguments
)
323 ComponentMethodGuard
aGuard( *this, ComponentMethodGuard::MethodType::WithoutInit
);
324 if ( impl_isInitialized_nothrow() )
325 throw AlreadyInitializedException();
327 sal_Int32 nArgumentCount
= _arguments
.getLength();
328 if ( ( nArgumentCount
!= 2 ) && ( nArgumentCount
!= 3 ) )
329 throw IllegalArgumentException("wrong number of args", static_cast<cppu::OWeakObject
*>(this), 1);
331 Type aKeyType
, aValueType
;
332 if ( !( _arguments
[0] >>= aKeyType
) )
333 throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 1 );
334 if ( !( _arguments
[1] >>= aValueType
) )
335 throw IllegalArgumentException("com.sun.star.uno.Type expected.", *this, 2 );
337 Sequence
< Pair
< Any
, Any
> > aInitialValues
;
338 bool bMutable
= true;
339 if ( nArgumentCount
== 3 )
341 if ( !( _arguments
[2] >>= aInitialValues
) )
342 throw IllegalArgumentException("[]com.sun.star.beans.Pair<any,any> expected.", *this, 2 );
346 // for the value, anything is allowed, except VOID
347 if ( ( aValueType
.getTypeClass() == TypeClass_VOID
) || ( aValueType
.getTypeClass() == TypeClass_UNKNOWN
) )
348 throw IllegalTypeException("Unsupported value type.", *this );
350 // create the comparator for the KeyType, and throw if the type is not supported
351 std::unique_ptr
< IKeyPredicateLess
> pComparator( getStandardLessPredicate( aKeyType
, nullptr ) );
353 throw IllegalTypeException("Unsupported key type.", *this );
356 m_aData
.m_aKeyType
= aKeyType
;
357 m_aData
.m_aValueType
= aValueType
;
358 m_aData
.m_pKeyCompare
= std::move(pComparator
);
359 m_aData
.m_pValues
.emplace( *m_aData
.m_pKeyCompare
);
360 m_aData
.m_bMutable
= bMutable
;
362 if ( aInitialValues
.hasElements() )
363 impl_initValues_throw( aInitialValues
);
369 void EnumerableMap::impl_initValues_throw( const Sequence
< Pair
< Any
, Any
> >& _initialValues
)
371 OSL_PRECOND( m_aData
.m_pValues
&& m_aData
.m_pValues
->empty(), "EnumerableMap::impl_initValues_throw: illegal call!" );
372 if (!m_aData
.m_pValues
|| !m_aData
.m_pValues
->empty())
373 throw RuntimeException();
375 const Pair
< Any
, Any
>* mapping
= _initialValues
.getConstArray();
376 const Pair
< Any
, Any
>* mappingEnd
= mapping
+ _initialValues
.getLength();
377 for ( ; mapping
!= mappingEnd
; ++mapping
)
379 impl_checkValue_throw( mapping
->Second
);
380 (*m_aData
.m_pValues
)[ mapping
->First
] = mapping
->Second
;
385 void EnumerableMap::impl_checkValue_throw( const Any
& _value
) const
387 if ( !_value
.hasValue() )
388 // nothing to do, NULL values are always allowed, regardless of the ValueType
391 TypeClass eAllowedTypeClass
= m_aData
.m_aValueType
.getTypeClass();
394 switch ( eAllowedTypeClass
)
397 bValid
= ( _value
.getValueTypeClass() == eAllowedTypeClass
);
402 case TypeClass_INTERFACE
:
404 // special treatment: _value might contain the proper type, but the interface
405 // might actually be NULL. Which is still valid ...
406 if ( m_aData
.m_aValueType
.isAssignableFrom( _value
.getValueType() ) )
407 // this also catches the special case where XFoo is our value type,
408 // and _value contains a NULL-reference to XFoo, or a derived type
412 Reference
< XInterface
> xValue( _value
, UNO_QUERY
);
414 // XInterface is not-NULL, but is X(ValueType) not-NULL, too?
415 xValue
.set( xValue
->queryInterface( m_aData
.m_aValueType
), UNO_QUERY
);
416 bValid
= xValue
.is();
420 case TypeClass_EXCEPTION
:
421 case TypeClass_STRUCT
:
423 // values are accepted if and only if their type equals, or is derived from, our value type
425 if ( _value
.getValueTypeClass() != eAllowedTypeClass
)
429 const TypeDescription
aValueTypeDesc( _value
.getValueType() );
430 const TypeDescription
aRequiredTypeDesc( m_aData
.m_aValueType
);
432 const _typelib_CompoundTypeDescription
* pValueCompoundTypeDesc
=
433 reinterpret_cast< const _typelib_CompoundTypeDescription
* >( aValueTypeDesc
.get() );
435 while ( pValueCompoundTypeDesc
)
437 if ( typelib_typedescription_equals( &pValueCompoundTypeDesc
->aBase
, aRequiredTypeDesc
.get() ) )
439 pValueCompoundTypeDesc
= pValueCompoundTypeDesc
->pBaseTypeDescription
;
441 bValid
= ( pValueCompoundTypeDesc
!= nullptr );
449 throw IllegalTypeException(
450 "Incompatible value type. Found '" + _value
.getValueTypeName()
451 + "', where '" + m_aData
.m_aValueType
.getTypeName()
452 + "' (or compatible type) is expected.",
453 *const_cast< EnumerableMap
* >( this ) );
456 impl_checkNaN_throw( _value
, m_aData
.m_aValueType
);
460 void EnumerableMap::impl_checkNaN_throw( const Any
& _keyOrValue
, const Type
& _keyOrValueType
) const
462 if ( ( _keyOrValueType
.getTypeClass() == TypeClass_DOUBLE
)
463 || ( _keyOrValueType
.getTypeClass() == TypeClass_FLOAT
)
467 if ( _keyOrValue
>>= nValue
)
468 if ( std::isnan( nValue
) )
469 throw IllegalArgumentException(
470 "NaN (not-a-number) not supported by this implementation.",
471 *const_cast< EnumerableMap
* >( this ), 0 );
472 // (note that the case of _key not containing a float/double value is handled in the
473 // respective IKeyPredicateLess implementation, so there's no need to handle this here.)
478 void EnumerableMap::impl_checkKey_throw( const Any
& _key
) const
480 if ( !_key
.hasValue() )
481 throw IllegalArgumentException(
482 "NULL keys not supported by this implementation.",
483 *const_cast< EnumerableMap
* >( this ), 0 );
485 impl_checkNaN_throw( _key
, m_aData
.m_aKeyType
);
489 void EnumerableMap::impl_checkMutable_throw() const
491 if ( !m_aData
.m_bMutable
)
492 throw NoSupportException(
493 "The map is immutable.",
494 *const_cast< EnumerableMap
* >( this ) );
498 Reference
< XEnumeration
> SAL_CALL
EnumerableMap::createKeyEnumeration( sal_Bool Isolated
)
500 ComponentMethodGuard
aGuard( *this );
501 return new MapEnumeration( *this, m_aData
, getBroadcastHelper(), eKeys
, Isolated
);
505 Reference
< XEnumeration
> SAL_CALL
EnumerableMap::createValueEnumeration( sal_Bool Isolated
)
507 ComponentMethodGuard
aGuard( *this );
508 return new MapEnumeration( *this, m_aData
, getBroadcastHelper(), eValues
, Isolated
);
512 Reference
< XEnumeration
> SAL_CALL
EnumerableMap::createElementEnumeration( sal_Bool Isolated
)
514 ComponentMethodGuard
aGuard( *this );
515 return new MapEnumeration( *this, m_aData
, getBroadcastHelper(), eBoth
, Isolated
);
519 Type SAL_CALL
EnumerableMap::getKeyType()
521 ComponentMethodGuard
aGuard( *this );
522 return m_aData
.m_aKeyType
;
526 Type SAL_CALL
EnumerableMap::getValueType()
528 ComponentMethodGuard
aGuard( *this );
529 return m_aData
.m_aValueType
;
533 void SAL_CALL
EnumerableMap::clear( )
535 ComponentMethodGuard
aGuard( *this );
536 impl_checkMutable_throw();
538 m_aData
.m_pValues
->clear();
540 lcl_notifyMapDataListeners_nothrow( m_aData
);
544 sal_Bool SAL_CALL
EnumerableMap::containsKey( const Any
& _key
)
546 ComponentMethodGuard
aGuard( *this );
547 impl_checkKey_throw( _key
);
549 KeyedValues::const_iterator pos
= m_aData
.m_pValues
->find( _key
);
550 return ( pos
!= m_aData
.m_pValues
->end() );
554 sal_Bool SAL_CALL
EnumerableMap::containsValue( const Any
& _value
)
556 ComponentMethodGuard
aGuard( *this );
557 impl_checkValue_throw( _value
);
558 for (auto const& value
: *m_aData
.m_pValues
)
560 if ( value
.second
== _value
)
567 Any SAL_CALL
EnumerableMap::get( const Any
& _key
)
569 ComponentMethodGuard
aGuard( *this );
570 impl_checkKey_throw( _key
);
572 KeyedValues::const_iterator pos
= m_aData
.m_pValues
->find( _key
);
573 if ( pos
== m_aData
.m_pValues
->end() )
574 throw NoSuchElementException( anyToString( _key
), *this );
580 Any SAL_CALL
EnumerableMap::put( const Any
& _key
, const Any
& _value
)
582 ComponentMethodGuard
aGuard( *this );
583 impl_checkMutable_throw();
584 impl_checkKey_throw( _key
);
585 impl_checkValue_throw( _value
);
589 KeyedValues::iterator pos
= m_aData
.m_pValues
->find( _key
);
590 if ( pos
!= m_aData
.m_pValues
->end() )
592 previousValue
= pos
->second
;
593 pos
->second
= _value
;
597 (*m_aData
.m_pValues
)[ _key
] = _value
;
600 lcl_notifyMapDataListeners_nothrow( m_aData
);
602 return previousValue
;
606 Any SAL_CALL
EnumerableMap::remove( const Any
& _key
)
608 ComponentMethodGuard
aGuard( *this );
609 impl_checkMutable_throw();
610 impl_checkKey_throw( _key
);
614 KeyedValues::iterator pos
= m_aData
.m_pValues
->find( _key
);
615 if ( pos
!= m_aData
.m_pValues
->end() )
617 previousValue
= pos
->second
;
618 m_aData
.m_pValues
->erase( pos
);
621 lcl_notifyMapDataListeners_nothrow( m_aData
);
623 return previousValue
;
627 Type SAL_CALL
EnumerableMap::getElementType()
629 return ::cppu::UnoType
< Pair
< Any
, Any
> >::get();
633 sal_Bool SAL_CALL
EnumerableMap::hasElements()
635 ComponentMethodGuard
aGuard( *this );
636 return m_aData
.m_pValues
->empty();
640 OUString SAL_CALL
EnumerableMap::getImplementationName( )
642 return "org.openoffice.comp.comphelper.EnumerableMap";
645 sal_Bool SAL_CALL
EnumerableMap::supportsService( const OUString
& _serviceName
)
647 return cppu::supportsService(this, _serviceName
);
651 Sequence
< OUString
> SAL_CALL
EnumerableMap::getSupportedServiceNames( )
653 return { "com.sun.star.container.EnumerableMap" };
656 bool MapEnumerator::hasMoreElements()
659 throw DisposedException( OUString(), m_rParent
);
660 return m_mapPos
!= m_rMapData
.m_pValues
->end();
664 Any
MapEnumerator::nextElement()
667 throw DisposedException( OUString(), m_rParent
);
668 if ( m_mapPos
== m_rMapData
.m_pValues
->end() )
669 throw NoSuchElementException("No more elements.", m_rParent
);
674 case eKeys
: aNextElement
= m_mapPos
->first
; break;
675 case eValues
: aNextElement
= m_mapPos
->second
; break;
676 case eBoth
: aNextElement
<<= Pair
< Any
, Any
>( m_mapPos
->first
, m_mapPos
->second
); break;
683 void MapEnumerator::mapModified()
689 sal_Bool SAL_CALL
MapEnumeration::hasMoreElements( )
691 ComponentMethodGuard
aGuard( *this );
692 return m_aEnumerator
.hasMoreElements();
696 Any SAL_CALL
MapEnumeration::nextElement( )
698 ComponentMethodGuard
aGuard( *this );
699 return m_aEnumerator
.nextElement();
703 } // namespace comphelper
706 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
707 org_openoffice_comp_comphelper_EnumerableMap(
708 css::uno::XComponentContext
*, css::uno::Sequence
<css::uno::Any
> const&)
710 return cppu::acquire(new comphelper::EnumerableMap());
713 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */