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 "opropertybag.hxx"
23 #include <com/sun/star/beans/IllegalTypeException.hpp>
24 #include <com/sun/star/beans/PropertyAttribute.hpp>
25 #include <com/sun/star/beans/Property.hpp>
27 #include <comphelper/namedvaluecollection.hxx>
28 #include <cppuhelper/supportsservice.hxx>
30 #include <cppuhelper/exc_hlp.hxx>
35 namespace com::sun::star::uno
{ class XComponentContext
; }
37 using namespace ::com::sun::star
;
39 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
40 com_sun_star_comp_comphelper_OPropertyBag (
41 css::uno::XComponentContext
*,
42 css::uno::Sequence
<css::uno::Any
> const &)
44 return cppu::acquire(new comphelper::OPropertyBag());
51 using namespace ::com::sun::star::uno
;
52 using namespace ::com::sun::star::lang
;
53 using namespace ::com::sun::star::beans
;
54 using namespace ::com::sun::star::util
;
55 using namespace ::com::sun::star::container
;
57 OPropertyBag::OPropertyBag()
58 :OPropertyBag_PBase( GetBroadcastHelper(), this )
59 ,::cppu::IEventNotificationHook()
60 ,m_bAutoAddProperties( false )
61 ,m_NotifyListeners(m_aMutex
)
68 OPropertyBag::~OPropertyBag()
73 IMPLEMENT_FORWARD_XINTERFACE2( OPropertyBag
, OPropertyBag_Base
, OPropertyBag_PBase
)
74 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyBag
, OPropertyBag_Base
, OPropertyBag_PBase
)
76 void SAL_CALL
OPropertyBag::initialize( const Sequence
< Any
>& _rArguments
)
78 Sequence
< Type
> aTypes
;
79 bool AllowEmptyPropertyName(false);
80 bool AutomaticAddition(false);
82 if (_rArguments
.getLength() == 3
83 && (_rArguments
[0] >>= aTypes
)
84 && (_rArguments
[1] >>= AllowEmptyPropertyName
)
85 && (_rArguments
[2] >>= AutomaticAddition
))
90 std::insert_iterator
< TypeBag
>( m_aAllowedTypes
, m_aAllowedTypes
.begin() )
92 m_bAutoAddProperties
= AutomaticAddition
;
95 ::comphelper::NamedValueCollection
aArguments( _rArguments
);
97 if ( aArguments
.get_ensureType( "AllowedTypes", aTypes
) )
101 std::insert_iterator
< TypeBag
>( m_aAllowedTypes
, m_aAllowedTypes
.begin() )
104 aArguments
.get_ensureType( "AutomaticAddition", m_bAutoAddProperties
);
105 aArguments
.get_ensureType( "AllowEmptyPropertyName",
106 AllowEmptyPropertyName
);
108 if (AllowEmptyPropertyName
) {
109 m_aDynamicProperties
.setAllowEmptyPropertyName(
110 AllowEmptyPropertyName
);
114 OUString SAL_CALL
OPropertyBag::getImplementationName()
116 return OUString( "com.sun.star.comp.comphelper.OPropertyBag" );
119 sal_Bool SAL_CALL
OPropertyBag::supportsService( const OUString
& rServiceName
)
121 return cppu::supportsService(this, rServiceName
);
124 Sequence
< OUString
> SAL_CALL
OPropertyBag::getSupportedServiceNames( )
126 return { "com.sun.star.beans.PropertyBag" };
129 void OPropertyBag::fireEvents(
130 sal_Int32
* /*pnHandles*/,
133 bool bIgnoreRuntimeExceptionsWhileFiring
)
135 if (nCount
&& !bVetoable
) {
136 setModifiedImpl(true, bIgnoreRuntimeExceptionsWhileFiring
);
140 void OPropertyBag::setModifiedImpl(bool bModified
,
141 bool bIgnoreRuntimeExceptionsWhileFiring
)
143 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
144 ::osl::MutexGuard
aGuard( m_aMutex
);
145 m_isModified
= bModified
;
149 Reference
<XInterface
> xThis(*this);
150 EventObject
event(xThis
);
151 m_NotifyListeners
.notifyEach(
152 &XModifyListener::modified
, event
);
153 } catch (RuntimeException
&) {
154 if (!bIgnoreRuntimeExceptionsWhileFiring
) {
157 } catch (Exception
&) {
164 sal_Bool SAL_CALL
OPropertyBag::isModified()
166 ::osl::MutexGuard
aGuard( m_aMutex
);
170 void SAL_CALL
OPropertyBag::setModified( sal_Bool bModified
)
172 setModifiedImpl(bModified
, false);
175 void SAL_CALL
OPropertyBag::addModifyListener(
176 const Reference
< XModifyListener
> & xListener
)
178 m_NotifyListeners
.addInterface(xListener
);
181 void SAL_CALL
OPropertyBag::removeModifyListener(
182 const Reference
< XModifyListener
> & xListener
)
184 m_NotifyListeners
.removeInterface(xListener
);
188 Reference
< XPropertySetInfo
> SAL_CALL
OPropertyBag::getPropertySetInfo( )
190 return createPropertySetInfo( getInfoHelper() );
194 sal_Bool SAL_CALL
OPropertyBag::has( const Any
& /*aElement*/ )
196 // XSet is only a workaround for addProperty not being able to add default-void properties.
197 // So, everything of XSet except insert is implemented empty
202 void SAL_CALL
OPropertyBag::insert( const Any
& _element
)
204 // This is a workaround for addProperty not being able to add default-void properties.
205 // If we ever have a smarter XPropertyContainer::addProperty interface, we can remove this, ehm, well, hack.
207 if ( !( _element
>>= aProperty
) )
208 throw IllegalArgumentException( OUString(), *this, 1 );
211 osl::MutexGuard
g(m_aMutex
);
213 // check whether the type is allowed, everything else will be checked
214 // by m_aDynamicProperties
215 if (!m_aAllowedTypes
.empty()
216 && m_aAllowedTypes
.find(aProperty
.Type
) == m_aAllowedTypes
.end())
217 throw IllegalArgumentException(OUString(), *this, 1);
219 m_aDynamicProperties
.addVoidProperty(aProperty
.Name
, aProperty
.Type
, findFreeHandle(),
220 aProperty
.Attributes
);
222 // our property info is dirty
223 m_pArrayHelper
.reset();
229 void SAL_CALL
OPropertyBag::remove( const Any
& /*aElement*/ )
231 // XSet is only a workaround for addProperty not being able to add default-void properties.
232 // So, everything of XSet except insert is implemented empty
233 throw NoSuchElementException( OUString(), *this );
237 Reference
< XEnumeration
> SAL_CALL
OPropertyBag::createEnumeration( )
239 // XSet is only a workaround for addProperty not being able to add default-void properties.
240 // So, everything of XSet except insert is implemented empty
245 Type SAL_CALL
OPropertyBag::getElementType( )
247 // XSet is only a workaround for addProperty not being able to add default-void properties.
248 // So, everything of XSet except insert is implemented empty
253 sal_Bool SAL_CALL
OPropertyBag::hasElements( )
255 // XSet is only a workaround for addProperty not being able to add default-void properties.
256 // So, everything of XSet except insert is implemented empty
261 void SAL_CALL
OPropertyBag::getFastPropertyValue( Any
& _rValue
, sal_Int32 _nHandle
) const
263 m_aDynamicProperties
.getFastPropertyValue( _nHandle
, _rValue
);
266 sal_Bool SAL_CALL
OPropertyBag::convertFastPropertyValue( Any
& _rConvertedValue
, Any
& _rOldValue
, sal_Int32 _nHandle
, const Any
& _rValue
)
268 return m_aDynamicProperties
.convertFastPropertyValue( _nHandle
, _rValue
, _rConvertedValue
, _rOldValue
);
271 void SAL_CALL
OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle
, const Any
& rValue
)
273 m_aDynamicProperties
.setFastPropertyValue( nHandle
, rValue
);
277 ::cppu::IPropertyArrayHelper
& SAL_CALL
OPropertyBag::getInfoHelper()
281 Sequence
< Property
> aProperties
;
282 m_aDynamicProperties
.describeProperties( aProperties
);
283 m_pArrayHelper
.reset( new ::cppu::OPropertyArrayHelper( aProperties
) );
285 return *m_pArrayHelper
;
290 sal_Int32
OPropertyBag::findFreeHandle() const
292 const sal_Int32 nPrime
= 1009;
293 const sal_Int32 nSeed
= 11;
295 sal_Int32 nCheck
= nSeed
;
296 while ( m_aDynamicProperties
.hasPropertyByHandle( nCheck
) && ( nCheck
!= 1 ) )
298 nCheck
= ( nCheck
* nSeed
) % nPrime
;
302 { // uh ... we already have 1008 handles used up
303 // -> simply count upwards
304 while ( m_aDynamicProperties
.hasPropertyByHandle( nCheck
) )
312 void SAL_CALL
OPropertyBag::addProperty( const OUString
& _rName
, ::sal_Int16 _nAttributes
, const Any
& _rInitialValue
)
315 osl::MutexGuard
g(m_aMutex
);
317 // check whether the type is allowed, everything else will be checked
318 // by m_aDynamicProperties
319 const Type
& aPropertyType
= _rInitialValue
.getValueType();
320 if (_rInitialValue
.hasValue() && !m_aAllowedTypes
.empty()
321 && m_aAllowedTypes
.find(aPropertyType
) == m_aAllowedTypes
.end())
322 throw IllegalTypeException(OUString(), *this);
324 m_aDynamicProperties
.addProperty(_rName
, findFreeHandle(), _nAttributes
,
327 // our property info is dirty
328 m_pArrayHelper
.reset();
334 void SAL_CALL
OPropertyBag::removeProperty( const OUString
& _rName
)
337 osl::MutexGuard
g(m_aMutex
);
339 m_aDynamicProperties
.removeProperty(_rName
);
341 // our property info is dirty
342 m_pArrayHelper
.reset();
350 struct ComparePropertyValueByName
352 bool operator()( const PropertyValue
& _rLHS
, const PropertyValue
& _rRHS
)
354 return _rLHS
.Name
< _rRHS
.Name
;
358 template< typename CLASS
>
359 struct TransformPropertyToName
361 const OUString
& operator()( const CLASS
& _rProp
)
367 struct ExtractPropertyValue
369 const Any
& operator()( const PropertyValue
& _rProp
)
377 Sequence
< PropertyValue
> SAL_CALL
OPropertyBag::getPropertyValues( )
379 ::osl::MutexGuard
aGuard( m_aMutex
);
381 // all registered properties
382 Sequence
< Property
> aProperties
;
383 m_aDynamicProperties
.describeProperties( aProperties
);
386 Sequence
< OUString
> aNames( aProperties
.getLength() );
391 TransformPropertyToName
< Property
>()
395 Sequence
< Any
> aValues
;
398 aValues
= OPropertyBag_PBase::getPropertyValues( aNames
);
399 if ( aValues
.getLength() != aNames
.getLength() )
400 throw RuntimeException();
402 catch( const RuntimeException
& )
406 catch( const Exception
& )
411 // merge names and values, and retrieve the state/handle
412 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
414 Sequence
< PropertyValue
> aPropertyValues( aNames
.getLength() );
415 const OUString
* pName
= aNames
.getConstArray();
416 const OUString
* pNamesEnd
= aNames
.getConstArray() + aNames
.getLength();
417 const Any
* pValue
= aValues
.getArray();
418 PropertyValue
* pPropertyValue
= aPropertyValues
.getArray();
420 for ( ; pName
!= pNamesEnd
; ++pName
, ++pValue
, ++pPropertyValue
)
422 pPropertyValue
->Name
= *pName
;
423 pPropertyValue
->Handle
= rPropInfo
.getHandleByName( *pName
);
424 pPropertyValue
->Value
= *pValue
;
425 pPropertyValue
->State
= getPropertyStateByHandle( pPropertyValue
->Handle
);
428 return aPropertyValues
;
432 void OPropertyBag::impl_setPropertyValues_throw( const Sequence
< PropertyValue
>& _rProps
)
434 // sort (the XMultiPropertySet interface requires this)
435 Sequence
< PropertyValue
> aProperties( _rProps
);
439 ComparePropertyValueByName()
442 // a sequence of names
443 Sequence
< OUString
> aNames( aProperties
.getLength() );
448 TransformPropertyToName
< PropertyValue
>()
453 // check for unknown properties
454 // we cannot simply rely on the XMultiPropertySet::setPropertyValues
455 // implementation of our base class, since it does not throw
456 // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues
457 // does not allow to throw this exception, while XPropertyAccess::setPropertyValues
459 sal_Int32 nCount
= aNames
.getLength();
461 Sequence
< sal_Int32
> aHandles( nCount
);
462 sal_Int32
* pHandle
= aHandles
.getArray();
463 const PropertyValue
* pProperty
= aProperties
.getConstArray();
464 for ( const OUString
* pName
= aNames
.getConstArray();
465 pName
!= aNames
.getConstArray() + aNames
.getLength();
466 ++pName
, ++pHandle
, ++pProperty
469 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
470 *pHandle
= rPropInfo
.getHandleByName( *pName
);
471 if ( *pHandle
!= -1 )
474 // there's a property requested which we do not know
475 if ( m_bAutoAddProperties
)
478 sal_Int16
const nAttributes
= PropertyAttribute::BOUND
| PropertyAttribute::REMOVABLE
| PropertyAttribute::MAYBEDEFAULT
;
479 addProperty( *pName
, nAttributes
, pProperty
->Value
);
484 throw UnknownPropertyException( *pName
, *this );
487 // a sequence of values
488 Sequence
< Any
> aValues( aProperties
.getLength() );
493 ExtractPropertyValue()
496 setFastPropertyValues( nCount
, aHandles
.getArray(), aValues
.getConstArray(), nCount
);
498 catch( const PropertyVetoException
& ) { throw; }
499 catch( const IllegalArgumentException
& ) { throw; }
500 catch( const WrappedTargetException
& ) { throw; }
501 catch( const RuntimeException
& ) { throw; }
502 catch( const UnknownPropertyException
& ) { throw; }
503 catch( const Exception
& )
505 throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
510 void SAL_CALL
OPropertyBag::setPropertyValues( const Sequence
< PropertyValue
>& _rProps
)
512 ::osl::MutexGuard
aGuard( m_aMutex
);
513 impl_setPropertyValues_throw( _rProps
);
517 PropertyState
OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle
)
519 // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but
520 // assume they're always in DIRECT state.
521 // (Note that this probably would belong into the base class. However, this would mean we would need
522 // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but
523 // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the
524 // current phase. #i78593#
526 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
527 sal_Int16
nAttributes(0);
528 OSL_VERIFY( rPropInfo
.fillPropertyMembersByHandle( nullptr, &nAttributes
, _nHandle
) );
529 if ( ( nAttributes
& PropertyAttribute::MAYBEDEFAULT
) == 0 )
530 return PropertyState_DIRECT_VALUE
;
532 return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle
);
536 Any
OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle
) const
539 m_aDynamicProperties
.getPropertyDefaultByHandle( _nHandle
, aDefault
);
544 } // namespace comphelper
547 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */