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>
34 namespace com::sun::star::uno
{ class XComponentContext
; }
36 using namespace ::com::sun::star
;
38 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
39 com_sun_star_comp_comphelper_OPropertyBag (
40 css::uno::XComponentContext
*,
41 css::uno::Sequence
<css::uno::Any
> const &)
43 return cppu::acquire(new comphelper::OPropertyBag());
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::lang
;
52 using namespace ::com::sun::star::beans
;
53 using namespace ::com::sun::star::util
;
54 using namespace ::com::sun::star::container
;
56 OPropertyBag::OPropertyBag()
57 :OPropertyBag_PBase( GetBroadcastHelper(), this )
58 ,::cppu::IEventNotificationHook()
59 ,m_bAutoAddProperties( false )
60 ,m_NotifyListeners(m_aMutex
)
67 OPropertyBag::~OPropertyBag()
72 IMPLEMENT_FORWARD_XINTERFACE2( OPropertyBag
, OPropertyBag_Base
, OPropertyBag_PBase
)
73 IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyBag
, OPropertyBag_Base
, OPropertyBag_PBase
)
75 void SAL_CALL
OPropertyBag::initialize( const Sequence
< Any
>& _rArguments
)
77 Sequence
< Type
> aTypes
;
78 bool AllowEmptyPropertyName(false);
79 bool AutomaticAddition(false);
81 if (_rArguments
.getLength() == 3
82 && (_rArguments
[0] >>= aTypes
)
83 && (_rArguments
[1] >>= AllowEmptyPropertyName
)
84 && (_rArguments
[2] >>= AutomaticAddition
))
86 m_aAllowedTypes
.insert(std::cbegin(aTypes
), std::cend(aTypes
));
87 m_bAutoAddProperties
= AutomaticAddition
;
90 ::comphelper::NamedValueCollection
aArguments( _rArguments
);
92 if ( aArguments
.get_ensureType( "AllowedTypes", aTypes
) )
93 m_aAllowedTypes
.insert(std::cbegin(aTypes
), std::cend(aTypes
));
95 aArguments
.get_ensureType( "AutomaticAddition", m_bAutoAddProperties
);
96 aArguments
.get_ensureType( "AllowEmptyPropertyName",
97 AllowEmptyPropertyName
);
99 if (AllowEmptyPropertyName
) {
100 m_aDynamicProperties
.setAllowEmptyPropertyName(
101 AllowEmptyPropertyName
);
105 OUString SAL_CALL
OPropertyBag::getImplementationName()
107 return "com.sun.star.comp.comphelper.OPropertyBag";
110 sal_Bool SAL_CALL
OPropertyBag::supportsService( const OUString
& rServiceName
)
112 return cppu::supportsService(this, rServiceName
);
115 Sequence
< OUString
> SAL_CALL
OPropertyBag::getSupportedServiceNames( )
117 return { "com.sun.star.beans.PropertyBag" };
120 void OPropertyBag::fireEvents(
121 sal_Int32
* /*pnHandles*/,
124 bool bIgnoreRuntimeExceptionsWhileFiring
)
126 if (nCount
&& !bVetoable
) {
127 setModifiedImpl(true, bIgnoreRuntimeExceptionsWhileFiring
);
131 void OPropertyBag::setModifiedImpl(bool bModified
,
132 bool bIgnoreRuntimeExceptionsWhileFiring
)
134 { // do not lock mutex while notifying (#i93514#) to prevent deadlock
135 ::osl::MutexGuard
aGuard( m_aMutex
);
136 m_isModified
= bModified
;
142 Reference
<XInterface
> xThis(*this);
143 EventObject
event(xThis
);
144 m_NotifyListeners
.notifyEach(
145 &XModifyListener::modified
, event
);
146 } catch (RuntimeException
&) {
147 if (!bIgnoreRuntimeExceptionsWhileFiring
) {
150 } catch (Exception
&) {
156 sal_Bool SAL_CALL
OPropertyBag::isModified()
158 ::osl::MutexGuard
aGuard( m_aMutex
);
162 void SAL_CALL
OPropertyBag::setModified( sal_Bool bModified
)
164 setModifiedImpl(bModified
, false);
167 void SAL_CALL
OPropertyBag::addModifyListener(
168 const Reference
< XModifyListener
> & xListener
)
170 m_NotifyListeners
.addInterface(xListener
);
173 void SAL_CALL
OPropertyBag::removeModifyListener(
174 const Reference
< XModifyListener
> & xListener
)
176 m_NotifyListeners
.removeInterface(xListener
);
180 Reference
< XPropertySetInfo
> SAL_CALL
OPropertyBag::getPropertySetInfo( )
182 return createPropertySetInfo( getInfoHelper() );
186 sal_Bool SAL_CALL
OPropertyBag::has( const Any
& /*aElement*/ )
188 // XSet is only a workaround for addProperty not being able to add default-void properties.
189 // So, everything of XSet except insert is implemented empty
194 void SAL_CALL
OPropertyBag::insert( const Any
& _element
)
196 // This is a workaround for addProperty not being able to add default-void properties.
197 // If we ever have a smarter XPropertyContainer::addProperty interface, we can remove this, ehm, well, hack.
199 if ( !( _element
>>= aProperty
) )
200 throw IllegalArgumentException( "element is not Property", *this, 1 );
203 osl::MutexGuard
g(m_aMutex
);
205 // check whether the type is allowed, everything else will be checked
206 // by m_aDynamicProperties
207 if (!m_aAllowedTypes
.empty()
208 && m_aAllowedTypes
.find(aProperty
.Type
) == m_aAllowedTypes
.end())
209 throw IllegalArgumentException("not in list of allowed types", *this, 1);
211 m_aDynamicProperties
.addVoidProperty(aProperty
.Name
, aProperty
.Type
, findFreeHandle(),
212 aProperty
.Attributes
);
214 // our property info is dirty
215 m_pArrayHelper
.reset();
221 void SAL_CALL
OPropertyBag::remove( const Any
& /*aElement*/ )
223 // XSet is only a workaround for addProperty not being able to add default-void properties.
224 // So, everything of XSet except insert is implemented empty
225 throw NoSuchElementException( OUString(), *this );
229 Reference
< XEnumeration
> SAL_CALL
OPropertyBag::createEnumeration( )
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
237 Type SAL_CALL
OPropertyBag::getElementType( )
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 sal_Bool SAL_CALL
OPropertyBag::hasElements( )
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 void SAL_CALL
OPropertyBag::getFastPropertyValue( Any
& _rValue
, sal_Int32 _nHandle
) const
255 m_aDynamicProperties
.getFastPropertyValue( _nHandle
, _rValue
);
258 sal_Bool SAL_CALL
OPropertyBag::convertFastPropertyValue( Any
& _rConvertedValue
, Any
& _rOldValue
, sal_Int32 _nHandle
, const Any
& _rValue
)
260 return m_aDynamicProperties
.convertFastPropertyValue( _nHandle
, _rValue
, _rConvertedValue
, _rOldValue
);
263 void SAL_CALL
OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle
, const Any
& rValue
)
265 m_aDynamicProperties
.setFastPropertyValue( nHandle
, rValue
);
269 ::cppu::IPropertyArrayHelper
& SAL_CALL
OPropertyBag::getInfoHelper()
273 Sequence
< Property
> aProperties
;
274 m_aDynamicProperties
.describeProperties( aProperties
);
275 m_pArrayHelper
.reset( new ::cppu::OPropertyArrayHelper( aProperties
) );
277 return *m_pArrayHelper
;
282 sal_Int32
OPropertyBag::findFreeHandle() const
284 const sal_Int32 nPrime
= 1009;
285 const sal_Int32 nSeed
= 11;
287 sal_Int32 nCheck
= nSeed
;
288 while ( m_aDynamicProperties
.hasPropertyByHandle( nCheck
) && ( nCheck
!= 1 ) )
290 nCheck
= ( nCheck
* nSeed
) % nPrime
;
294 { // uh ... we already have 1008 handles used up
295 // -> simply count upwards
296 while ( m_aDynamicProperties
.hasPropertyByHandle( nCheck
) )
304 void SAL_CALL
OPropertyBag::addProperty( const OUString
& _rName
, ::sal_Int16 _nAttributes
, const Any
& _rInitialValue
)
307 osl::MutexGuard
g(m_aMutex
);
309 // check whether the type is allowed, everything else will be checked
310 // by m_aDynamicProperties
311 const Type
& aPropertyType
= _rInitialValue
.getValueType();
312 if (_rInitialValue
.hasValue() && !m_aAllowedTypes
.empty()
313 && m_aAllowedTypes
.find(aPropertyType
) == m_aAllowedTypes
.end())
314 throw IllegalTypeException(OUString(), *this);
316 m_aDynamicProperties
.addProperty(_rName
, findFreeHandle(), _nAttributes
,
319 // our property info is dirty
320 m_pArrayHelper
.reset();
326 void SAL_CALL
OPropertyBag::removeProperty( const OUString
& _rName
)
329 osl::MutexGuard
g(m_aMutex
);
331 m_aDynamicProperties
.removeProperty(_rName
);
333 // our property info is dirty
334 m_pArrayHelper
.reset();
342 struct ComparePropertyValueByName
344 bool operator()( const PropertyValue
& _rLHS
, const PropertyValue
& _rRHS
)
346 return _rLHS
.Name
< _rRHS
.Name
;
350 template< typename CLASS
>
351 struct TransformPropertyToName
353 const OUString
& operator()( const CLASS
& _rProp
)
359 struct ExtractPropertyValue
361 const Any
& operator()( const PropertyValue
& _rProp
)
369 Sequence
< PropertyValue
> SAL_CALL
OPropertyBag::getPropertyValues( )
371 ::osl::MutexGuard
aGuard( m_aMutex
);
373 // all registered properties
374 Sequence
< Property
> aProperties
;
375 m_aDynamicProperties
.describeProperties( aProperties
);
378 Sequence
< OUString
> aNames( aProperties
.getLength() );
380 std::cbegin(aProperties
),
381 std::cend(aProperties
),
383 TransformPropertyToName
< Property
>()
387 Sequence
< Any
> aValues
;
390 aValues
= OPropertyBag_PBase::getPropertyValues( aNames
);
391 if ( aValues
.getLength() != aNames
.getLength() )
392 throw RuntimeException();
394 catch( const RuntimeException
& )
398 catch( const Exception
& )
403 // merge names and values, and retrieve the state/handle
404 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
406 Sequence
< PropertyValue
> aPropertyValues( aNames
.getLength() );
407 const OUString
* pName
= aNames
.getConstArray();
408 const OUString
* pNamesEnd
= aNames
.getConstArray() + aNames
.getLength();
409 const Any
* pValue
= aValues
.getArray();
410 PropertyValue
* pPropertyValue
= aPropertyValues
.getArray();
412 for ( ; pName
!= pNamesEnd
; ++pName
, ++pValue
, ++pPropertyValue
)
414 pPropertyValue
->Name
= *pName
;
415 pPropertyValue
->Handle
= rPropInfo
.getHandleByName( *pName
);
416 pPropertyValue
->Value
= *pValue
;
417 pPropertyValue
->State
= getPropertyStateByHandle( pPropertyValue
->Handle
);
420 return aPropertyValues
;
424 void OPropertyBag::impl_setPropertyValues_throw( const Sequence
< PropertyValue
>& _rProps
)
426 // sort (the XMultiPropertySet interface requires this)
427 Sequence
< PropertyValue
> aProperties( _rProps
);
428 auto [begin
, end
] = asNonConstRange(aProperties
);
432 ComparePropertyValueByName()
435 // a sequence of names
436 Sequence
< OUString
> aNames( aProperties
.getLength() );
438 std::cbegin(aProperties
),
439 std::cend(aProperties
),
441 TransformPropertyToName
< PropertyValue
>()
446 // check for unknown properties
447 // we cannot simply rely on the XMultiPropertySet::setPropertyValues
448 // implementation of our base class, since it does not throw
449 // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues
450 // does not allow to throw this exception, while XPropertyAccess::setPropertyValues
452 sal_Int32 nCount
= aNames
.getLength();
454 Sequence
< sal_Int32
> aHandles( nCount
);
455 sal_Int32
* pHandle
= aHandles
.getArray();
456 const PropertyValue
* pProperty
= aProperties
.getConstArray();
457 for ( const OUString
* pName
= aNames
.getConstArray();
458 pName
!= aNames
.getConstArray() + aNames
.getLength();
459 ++pName
, ++pHandle
, ++pProperty
462 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
463 *pHandle
= rPropInfo
.getHandleByName( *pName
);
464 if ( *pHandle
!= -1 )
467 // there's a property requested which we do not know
468 if ( m_bAutoAddProperties
)
471 sal_Int16
const nAttributes
= PropertyAttribute::BOUND
| PropertyAttribute::REMOVABLE
| PropertyAttribute::MAYBEDEFAULT
;
472 addProperty( *pName
, nAttributes
, pProperty
->Value
);
477 throw UnknownPropertyException( *pName
, *this );
480 // a sequence of values
481 Sequence
< Any
> aValues( aProperties
.getLength() );
483 std::cbegin(aProperties
),
484 std::cend(aProperties
),
486 ExtractPropertyValue()
489 setFastPropertyValues( nCount
, aHandles
.getArray(), aValues
.getConstArray(), nCount
);
491 catch( const PropertyVetoException
& ) { throw; }
492 catch( const IllegalArgumentException
& ) { throw; }
493 catch( const WrappedTargetException
& ) { throw; }
494 catch( const RuntimeException
& ) { throw; }
495 catch( const UnknownPropertyException
& ) { throw; }
496 catch( const Exception
& )
498 throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
503 void SAL_CALL
OPropertyBag::setPropertyValues( const Sequence
< PropertyValue
>& _rProps
)
505 ::osl::MutexGuard
aGuard( m_aMutex
);
506 impl_setPropertyValues_throw( _rProps
);
510 PropertyState
OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle
)
512 // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but
513 // assume they're always in DIRECT state.
514 // (Note that this probably would belong into the base class. However, this would mean we would need
515 // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but
516 // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the
517 // current phase. #i78593#
519 ::cppu::IPropertyArrayHelper
& rPropInfo
= getInfoHelper();
520 sal_Int16
nAttributes(0);
521 OSL_VERIFY( rPropInfo
.fillPropertyMembersByHandle( nullptr, &nAttributes
, _nHandle
) );
522 if ( ( nAttributes
& PropertyAttribute::MAYBEDEFAULT
) == 0 )
523 return PropertyState_DIRECT_VALUE
;
525 return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle
);
529 Any
OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle
) const
532 m_aDynamicProperties
.getPropertyDefaultByHandle( _nHandle
, aDefault
);
537 } // namespace comphelper
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */