Simplify a bit
[LibreOffice.git] / comphelper / source / property / opropertybag.cxx
blob3b4a0498dcef196f54209e7ec0e00b576814c639
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
32 #include <algorithm>
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());
46 namespace comphelper
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)
61 ,m_isModified(false)
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;
89 } else {
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*/,
122 sal_Int32 nCount,
123 sal_Bool bVetoable,
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;
138 if (!bModified)
139 return;
141 try {
142 Reference<XInterface> xThis(*this);
143 EventObject event(xThis);
144 m_NotifyListeners.notifyEach(
145 &XModifyListener::modified, event);
146 } catch (RuntimeException &) {
147 if (!bIgnoreRuntimeExceptionsWhileFiring) {
148 throw;
150 } catch (Exception &) {
151 // ignore
156 sal_Bool SAL_CALL OPropertyBag::isModified()
158 ::osl::MutexGuard aGuard( m_aMutex );
159 return m_isModified;
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
190 return false;
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.
198 Property aProperty;
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();
217 setModified(true);
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
233 return nullptr;
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
241 return Type();
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
249 return false;
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()
271 if (!m_pArrayHelper)
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;
293 if ( nCheck == 1 )
294 { // uh ... we already have 1008 handles used up
295 // -> simply count upwards
296 while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) )
297 ++nCheck;
300 return 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,
317 _rInitialValue);
319 // our property info is dirty
320 m_pArrayHelper.reset();
322 setModified(true);
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();
336 setModified(true);
340 namespace
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 )
355 return _rProp.Name;
359 struct ExtractPropertyValue
361 const Any& operator()( const PropertyValue& _rProp )
363 return _rProp.Value;
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 );
377 // their names
378 Sequence< OUString > aNames( aProperties.getLength() );
379 std::transform(
380 std::cbegin(aProperties),
381 std::cend(aProperties),
382 aNames.getArray(),
383 TransformPropertyToName< Property >()
386 // their values
387 Sequence< Any > aValues;
390 aValues = OPropertyBag_PBase::getPropertyValues( aNames );
391 if ( aValues.getLength() != aNames.getLength() )
392 throw RuntimeException("property name and value counts out of sync");
394 catch( const RuntimeException& )
396 throw;
398 catch( const Exception& )
400 // ignore
403 // merge names and values, and retrieve the state/handle
404 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
406 Sequence< PropertyValue > aPropertyValues( aNames.getLength() );
407 PropertyValue* pPropertyValue = aPropertyValues.getArray();
409 for (sal_Int32 i = 0; i < aNames.getLength(); ++i)
411 pPropertyValue[i].Name = aNames[i];
412 pPropertyValue[i].Handle = rPropInfo.getHandleByName(aNames[i]);
413 pPropertyValue[i].Value = aValues[i];
414 pPropertyValue[i].State = getPropertyStateByHandle(pPropertyValue[i].Handle);
417 return aPropertyValues;
421 void OPropertyBag::impl_setPropertyValues_throw( const Sequence< PropertyValue >& _rProps )
423 // sort (the XMultiPropertySet interface requires this)
424 Sequence< PropertyValue > aProperties( _rProps );
425 auto [begin, end] = asNonConstRange(aProperties);
426 std::sort(
427 begin,
428 end,
429 ComparePropertyValueByName()
432 // a sequence of names
433 Sequence< OUString > aNames( aProperties.getLength() );
434 std::transform(
435 std::cbegin(aProperties),
436 std::cend(aProperties),
437 aNames.getArray(),
438 TransformPropertyToName< PropertyValue >()
443 // check for unknown properties
444 // we cannot simply rely on the XMultiPropertySet::setPropertyValues
445 // implementation of our base class, since it does not throw
446 // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues
447 // does not allow to throw this exception, while XPropertyAccess::setPropertyValues
448 // requires it
449 sal_Int32 nCount = aNames.getLength();
451 Sequence< sal_Int32 > aHandles( nCount );
452 sal_Int32* pHandles = aHandles.getArray();
453 for (sal_Int32 i = 0; i < nCount; ++i)
455 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
456 pHandles[i] = rPropInfo.getHandleByName(aNames[i]);
457 if (pHandles[i] != -1)
458 continue;
460 // there's a property requested which we do not know
461 if ( m_bAutoAddProperties )
463 // add the property
464 sal_Int16 const nAttributes = PropertyAttribute::BOUND | PropertyAttribute::REMOVABLE | PropertyAttribute::MAYBEDEFAULT;
465 addProperty(aNames[i], nAttributes, aProperties[i].Value);
466 continue;
469 // no way out
470 throw UnknownPropertyException(aNames[i], *this);
473 // a sequence of values
474 Sequence< Any > aValues( aProperties.getLength() );
475 std::transform(
476 std::cbegin(aProperties),
477 std::cend(aProperties),
478 aValues.getArray(),
479 ExtractPropertyValue()
482 setFastPropertyValues(nCount, pHandles, aValues.getConstArray(), nCount);
484 catch( const PropertyVetoException& ) { throw; }
485 catch( const IllegalArgumentException& ) { throw; }
486 catch( const WrappedTargetException& ) { throw; }
487 catch( const RuntimeException& ) { throw; }
488 catch( const UnknownPropertyException& ) { throw; }
489 catch( const Exception& )
491 throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
496 void SAL_CALL OPropertyBag::setPropertyValues( const Sequence< PropertyValue >& _rProps )
498 ::osl::MutexGuard aGuard( m_aMutex );
499 impl_setPropertyValues_throw( _rProps );
503 PropertyState OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle )
505 // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but
506 // assume they're always in DIRECT state.
507 // (Note that this probably would belong into the base class. However, this would mean we would need
508 // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but
509 // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the
510 // current phase. #i78593#
512 ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
513 sal_Int16 nAttributes(0);
514 OSL_VERIFY( rPropInfo.fillPropertyMembersByHandle( nullptr, &nAttributes, _nHandle ) );
515 if ( ( nAttributes & PropertyAttribute::MAYBEDEFAULT ) == 0 )
516 return PropertyState_DIRECT_VALUE;
518 return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle );
522 Any OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
524 Any aDefault;
525 m_aDynamicProperties.getPropertyDefaultByHandle( _nHandle, aDefault );
526 return aDefault;
530 } // namespace comphelper
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */