summaryrefslogtreecommitdiffstats
path: root/comphelper/source/property
diff options
context:
space:
mode:
Diffstat (limited to 'comphelper/source/property')
-rw-r--r--comphelper/source/property/ChainablePropertySet.cxx239
-rw-r--r--comphelper/source/property/ChainablePropertySetInfo.cxx95
-rw-r--r--comphelper/source/property/MasterPropertySet.cxx398
-rw-r--r--comphelper/source/property/MasterPropertySetInfo.cxx105
-rw-r--r--comphelper/source/property/genericpropertyset.cxx247
-rw-r--r--comphelper/source/property/opropertybag.cxx540
-rw-r--r--comphelper/source/property/opropertybag.hxx218
-rw-r--r--comphelper/source/property/propagg.cxx870
-rw-r--r--comphelper/source/property/property.cxx203
-rw-r--r--comphelper/source/property/propertybag.cxx206
-rw-r--r--comphelper/source/property/propertycontainer.cxx76
-rw-r--r--comphelper/source/property/propertycontainerhelper.cxx494
-rw-r--r--comphelper/source/property/propertysethelper.cxx271
-rw-r--r--comphelper/source/property/propertysetinfo.cxx120
-rw-r--r--comphelper/source/property/propertystatecontainer.cxx183
-rw-r--r--comphelper/source/property/propmultiplex.cxx153
-rw-r--r--comphelper/source/property/propstate.cxx228
17 files changed, 4646 insertions, 0 deletions
diff --git a/comphelper/source/property/ChainablePropertySet.cxx b/comphelper/source/property/ChainablePropertySet.cxx
new file mode 100644
index 000000000..0805afe72
--- /dev/null
+++ b/comphelper/source/property/ChainablePropertySet.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/ChainablePropertySet.hxx>
+#include <comphelper/ChainablePropertySetInfo.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+#include <memory>
+#include <optional>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+ChainablePropertySet::ChainablePropertySet( comphelper::ChainablePropertySetInfo* pInfo, comphelper::SolarMutex* pMutex )
+ noexcept
+: mpMutex ( pMutex )
+, mxInfo ( pInfo )
+{
+}
+
+ChainablePropertySet::~ChainablePropertySet()
+ noexcept
+{
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL ChainablePropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void SAL_CALL ChainablePropertySet::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ _preSetValues();
+ _setSingleValue( *((*aIter).second), rValue );
+ _postSetValues();
+}
+
+Any SAL_CALL ChainablePropertySet::getPropertyValue( const OUString& rPropertyName )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ Any aAny;
+ _preGetValues ();
+ _getSingleValue( *((*aIter).second), aAny );
+ _postGetValues ();
+
+ return aAny;
+}
+
+void SAL_CALL ChainablePropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL ChainablePropertySet::setPropertyValues(const Sequence< OUString >& rPropertyNames, const Sequence< Any >& rValues)
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( nCount != rValues.getLength() )
+ throw IllegalArgumentException("lengths do not match", static_cast<cppu::OWeakObject*>(this), -1);
+
+ if( !nCount )
+ return;
+
+ _preSetValues();
+
+ const Any * pAny = rValues.getConstArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ _setSingleValue ( *((*aIter).second), *pAny );
+ }
+
+ _postSetValues();
+}
+
+Sequence< Any > SAL_CALL ChainablePropertySet::getPropertyValues(const Sequence< OUString >& rPropertyNames)
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence < Any > aValues ( nCount );
+
+ if( nCount )
+ {
+ _preGetValues();
+
+ Any * pAny = aValues.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ _getSingleValue ( *((*aIter).second), *pAny );
+ }
+
+ _postGetValues();
+ }
+ return aValues;
+}
+
+void SAL_CALL ChainablePropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL ChainablePropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL ChainablePropertySet::getPropertyState( const OUString& PropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find( PropertyName );
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ return PropertyState_AMBIGUOUS_VALUE;
+}
+
+Sequence< PropertyState > SAL_CALL ChainablePropertySet::getPropertyStates( const Sequence< OUString >& rPropertyNames )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+ if( nCount )
+ {
+ PropertyState * pState = aStates.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyInfoHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pState )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw UnknownPropertyException( *pString, static_cast< XPropertySet* >( this ) );
+ }
+ }
+ return aStates;
+}
+
+void SAL_CALL ChainablePropertySet::setPropertyToDefault( const OUString& rPropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+}
+
+Any SAL_CALL ChainablePropertySet::getPropertyDefault( const OUString& rPropertyName )
+{
+ PropertyInfoHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+ return Any();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/ChainablePropertySetInfo.cxx b/comphelper/source/property/ChainablePropertySetInfo.cxx
new file mode 100644
index 000000000..10b4d5fda
--- /dev/null
+++ b/comphelper/source/property/ChainablePropertySetInfo.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/ChainablePropertySetInfo.hxx>
+#include <sal/log.hxx>
+
+using ::comphelper::PropertyInfo;
+using ::comphelper::ChainablePropertySetInfo;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::beans::UnknownPropertyException;
+
+ChainablePropertySetInfo::ChainablePropertySetInfo( PropertyInfo const * pMap )
+{
+ for( ; !pMap->maName.isEmpty(); ++pMap )
+ {
+ SAL_WARN_IF(
+ maMap.find(pMap->maName) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << pMap->maName << "\"");
+ maMap[pMap->maName] = pMap;
+ }
+}
+
+ChainablePropertySetInfo::~ChainablePropertySetInfo()
+ noexcept
+{
+}
+
+void ChainablePropertySetInfo::remove( const OUString& aName )
+{
+ maMap.erase ( aName );
+ if ( maProperties.hasElements() )
+ maProperties.realloc( 0 );
+}
+
+Sequence< ::Property > SAL_CALL ChainablePropertySetInfo::getProperties()
+{
+ sal_Int32 nSize = maMap.size();
+ if( maProperties.getLength() != nSize )
+ {
+ maProperties.realloc ( nSize );
+ Property* pProperties = maProperties.getArray();
+
+ for (auto const& elem : maMap)
+ {
+ PropertyInfo const * pInfo = elem.second;
+
+ pProperties->Name = pInfo->maName;
+ pProperties->Handle = pInfo->mnHandle;
+ pProperties->Type = pInfo->maType;
+ pProperties->Attributes = pInfo->mnAttributes;
+ ++pProperties;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL ChainablePropertySetInfo::getPropertyByName( const OUString& rName )
+{
+ PropertyInfoHash::iterator aIter = maMap.find( rName );
+
+ if ( maMap.end() == aIter )
+ throw UnknownPropertyException( rName, *this );
+
+ PropertyInfo const *pInfo = (*aIter).second;
+ Property aProperty;
+ aProperty.Name = pInfo->maName;
+ aProperty.Handle = pInfo->mnHandle;
+ aProperty.Type = pInfo->maType;
+ aProperty.Attributes = pInfo->mnAttributes;
+ return aProperty;
+}
+
+sal_Bool SAL_CALL ChainablePropertySetInfo::hasPropertyByName( const OUString& rName )
+{
+ return maMap.find ( rName ) != maMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/MasterPropertySet.cxx b/comphelper/source/property/MasterPropertySet.cxx
new file mode 100644
index 000000000..922a4c1c6
--- /dev/null
+++ b/comphelper/source/property/MasterPropertySet.cxx
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <comphelper/MasterPropertySet.hxx>
+#include <comphelper/MasterPropertySetInfo.hxx>
+#include <comphelper/ChainablePropertySet.hxx>
+#include <comphelper/ChainablePropertySetInfo.hxx>
+#include <comphelper/solarmutex.hxx>
+
+
+#include <memory>
+#include <vector>
+#include <optional>
+
+namespace {
+
+class AutoOGuardArray
+{
+ std::vector<std::optional< osl::Guard< comphelper::SolarMutex > >> maGuardArray;
+
+public:
+ explicit AutoOGuardArray( sal_Int32 nNumElements );
+
+ std::optional< osl::Guard< comphelper::SolarMutex > > & operator[] ( sal_Int32 i ) { return maGuardArray[i]; }
+};
+
+}
+
+AutoOGuardArray::AutoOGuardArray( sal_Int32 nNumElements ) : maGuardArray(nNumElements)
+{
+}
+
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+
+SlaveData::SlaveData ( ChainablePropertySet *pSlave)
+: mxSlave ( pSlave )
+, mbInit ( false )
+{
+}
+
+MasterPropertySet::MasterPropertySet( comphelper::MasterPropertySetInfo* pInfo, comphelper::SolarMutex* pMutex )
+ noexcept
+: mpMutex ( pMutex )
+, mnLastId ( 0 )
+, mxInfo ( pInfo )
+{
+}
+
+MasterPropertySet::~MasterPropertySet()
+ noexcept
+{
+ for( const auto& rSlave : maSlaveMap )
+ delete rSlave.second;
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL MasterPropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void MasterPropertySet::registerSlave ( ChainablePropertySet *pNewSet )
+ noexcept
+{
+ maSlaveMap [ ++mnLastId ] = new SlaveData ( pNewSet );
+ mxInfo->add ( pNewSet->mxInfo->maMap, mnLastId );
+}
+
+void SAL_CALL MasterPropertySet::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ {
+ _preSetValues();
+ _setSingleValue( *((*aIter).second->mpInfo), rValue );
+ _postSetValues();
+ }
+ else
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard2;
+ if (pSlave->mpMutex)
+ xMutexGuard2.emplace( pSlave->mpMutex );
+
+ pSlave->_preSetValues();
+ pSlave->_setSingleValue( *((*aIter).second->mpInfo), rValue );
+ pSlave->_postSetValues();
+ }
+}
+
+Any SAL_CALL MasterPropertySet::getPropertyValue( const OUString& rPropertyName )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+
+ Any aAny;
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ {
+ _preGetValues();
+ _getSingleValue( *((*aIter).second->mpInfo), aAny );
+ _postGetValues();
+ }
+ else
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard2;
+ if (pSlave->mpMutex)
+ xMutexGuard2.emplace( pSlave->mpMutex );
+
+ pSlave->_preGetValues();
+ pSlave->_getSingleValue( *((*aIter).second->mpInfo), aAny );
+ pSlave->_postGetValues();
+ }
+ return aAny;
+}
+
+void SAL_CALL MasterPropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL MasterPropertySet::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ if( nCount != aValues.getLength() )
+ throw IllegalArgumentException();
+
+ if( !nCount )
+ return;
+
+ _preSetValues();
+
+ const Any * pAny = aValues.getConstArray();
+ const OUString * pString = aPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ //!! have a unique_ptr to an array of OGuards in order to have the
+ //!! allocated memory properly freed (exception safe!).
+ //!! Since the array itself has unique_ptrs as members we have to use a
+ //!! helper class 'AutoOGuardArray' in order to have
+ //!! the acquired locks properly released.
+ AutoOGuardArray aOGuardArray( nCount );
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ _setSingleValue( *((*aIter).second->mpInfo), *pAny );
+ else
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ if (pSlave->mxSlave->mpMutex)
+ aOGuardArray[i].emplace( pSlave->mxSlave->mpMutex );
+
+ pSlave->mxSlave->_preSetValues();
+ pSlave->SetInit ( true );
+ }
+ pSlave->mxSlave->_setSingleValue( *((*aIter).second->mpInfo), *pAny );
+ }
+ }
+
+ _postSetValues();
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->mxSlave->_postSetValues();
+ rSlave.second->SetInit( false );
+ }
+ }
+}
+
+Sequence< Any > SAL_CALL MasterPropertySet::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (mpMutex)
+ xMutexGuard.emplace( mpMutex );
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ Sequence < Any > aValues ( nCount );
+
+ if( nCount )
+ {
+ _preGetValues();
+
+ Any * pAny = aValues.getArray();
+ const OUString * pString = aPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ //!! have a unique_ptr to an array of OGuards in order to have the
+ //!! allocated memory properly freed (exception safe!).
+ //!! Since the array itself has unique_ptrs as members we have to use a
+ //!! helper class 'AutoOGuardArray' in order to have
+ //!! the acquired locks properly released.
+ AutoOGuardArray aOGuardArray( nCount );
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pAny )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw RuntimeException( *pString, static_cast< XPropertySet* >( this ) );
+
+ if ( (*aIter).second->mnMapId == 0 ) // 0 means it's one of ours !
+ _getSingleValue( *((*aIter).second->mpInfo), *pAny );
+ else
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ if (pSlave->mxSlave->mpMutex)
+ aOGuardArray[i].emplace( pSlave->mxSlave->mpMutex );
+
+ pSlave->mxSlave->_preGetValues();
+ pSlave->SetInit ( true );
+ }
+ pSlave->mxSlave->_getSingleValue( *((*aIter).second->mpInfo), *pAny );
+ }
+ }
+
+ _postSetValues();
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->mxSlave->_postSetValues();
+ rSlave.second->SetInit( false );
+ }
+ }
+ }
+ return aValues;
+}
+
+void SAL_CALL MasterPropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL MasterPropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL MasterPropertySet::getPropertyState( const OUString& PropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find( PropertyName );
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ // 0 means it's one of ours !
+ if ( (*aIter).second->mnMapId != 0 )
+ {
+ ChainablePropertySet * pSlave = maSlaveMap [ (*aIter).second->mnMapId ]->mxSlave.get();
+
+ // acquire mutex in c-tor and releases it in the d-tor (exception safe!).
+ std::optional< osl::Guard< comphelper::SolarMutex > > xMutexGuard;
+ if (pSlave->mpMutex)
+ xMutexGuard.emplace( pSlave->mpMutex );
+ }
+
+ return PropertyState_AMBIGUOUS_VALUE;
+}
+
+Sequence< PropertyState > SAL_CALL MasterPropertySet::getPropertyStates( const Sequence< OUString >& rPropertyNames )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+ if( nCount )
+ {
+ PropertyState * pState = aStates.getArray();
+ const OUString * pString = rPropertyNames.getConstArray();
+ PropertyDataHash::const_iterator aEnd = mxInfo->maMap.end(), aIter;
+
+ for ( sal_Int32 i = 0; i < nCount; ++i, ++pString, ++pState )
+ {
+ aIter = mxInfo->maMap.find ( *pString );
+ if ( aIter == aEnd )
+ throw UnknownPropertyException( *pString, static_cast< XPropertySet* >( this ) );
+
+ // 0 means it's one of ours !
+ if ( (*aIter).second->mnMapId != 0 )
+ {
+ SlaveData * pSlave = maSlaveMap [ (*aIter).second->mnMapId ];
+ if (!pSlave->IsInit())
+ {
+ pSlave->SetInit ( true );
+ }
+ }
+ }
+ for( const auto& rSlave : maSlaveMap )
+ {
+ if( rSlave.second->IsInit() )
+ {
+ rSlave.second->SetInit( false );
+ }
+ }
+ }
+ return aStates;
+}
+
+void SAL_CALL MasterPropertySet::setPropertyToDefault( const OUString& rPropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+}
+
+Any SAL_CALL MasterPropertySet::getPropertyDefault( const OUString& rPropertyName )
+{
+ PropertyDataHash::const_iterator aIter = mxInfo->maMap.find ( rPropertyName );
+
+ if( aIter == mxInfo->maMap.end())
+ throw UnknownPropertyException( rPropertyName, static_cast< XPropertySet* >( this ) );
+ return Any();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/MasterPropertySetInfo.cxx b/comphelper/source/property/MasterPropertySetInfo.cxx
new file mode 100644
index 000000000..db8ddb769
--- /dev/null
+++ b/comphelper/source/property/MasterPropertySetInfo.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/MasterPropertySetInfo.hxx>
+#include <sal/log.hxx>
+
+using ::comphelper::PropertyInfo;
+using ::comphelper::MasterPropertySetInfo;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::Property;
+using ::com::sun::star::beans::UnknownPropertyException;
+
+MasterPropertySetInfo::MasterPropertySetInfo( PropertyInfo const * pMap )
+{
+ for ( ; !pMap->maName.isEmpty(); ++pMap )
+ {
+ SAL_WARN_IF(
+ maMap.find(pMap->maName) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << pMap->maName << "\"");
+ maMap[pMap->maName] = new PropertyData ( 0, pMap );
+ }
+}
+
+MasterPropertySetInfo::~MasterPropertySetInfo()
+ noexcept
+{
+ for( const auto& rObj : maMap )
+ delete rObj.second;
+}
+
+void MasterPropertySetInfo::add( PropertyInfoHash &rHash, sal_uInt8 nMapId )
+{
+ if( maProperties.hasElements() )
+ maProperties.realloc( 0 );
+
+ for( const auto& rObj : rHash )
+ {
+ SAL_WARN_IF(
+ maMap.find(rObj.first) != maMap.end(),
+ "comphelper", "Duplicate property name \"" << rObj.first << "\"");
+ maMap[rObj.first] = new PropertyData ( nMapId, rObj.second );
+ }
+}
+
+Sequence< ::Property > SAL_CALL MasterPropertySetInfo::getProperties()
+{
+ sal_Int32 nSize = maMap.size();
+ if( maProperties.getLength() != nSize )
+ {
+ maProperties.realloc ( nSize );
+ Property* pProperties = maProperties.getArray();
+
+ for (auto const& elem : maMap)
+ {
+ PropertyInfo const * pInfo = elem.second->mpInfo;
+
+ pProperties->Name = pInfo->maName;
+ pProperties->Handle = pInfo->mnHandle;
+ pProperties->Type = pInfo->maType;
+ pProperties->Attributes = pInfo->mnAttributes;
+ ++pProperties;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL MasterPropertySetInfo::getPropertyByName( const OUString& rName )
+{
+ PropertyDataHash::iterator aIter = maMap.find( rName );
+
+ if ( maMap.end() == aIter )
+ throw UnknownPropertyException( rName, *this );
+
+ PropertyInfo const *pInfo = (*aIter).second->mpInfo;
+ Property aProperty;
+ aProperty.Name = pInfo->maName;
+ aProperty.Handle = pInfo->mnHandle;
+ aProperty.Type = pInfo->maType;
+
+ aProperty.Attributes = pInfo->mnAttributes;
+ return aProperty;
+}
+
+sal_Bool SAL_CALL MasterPropertySetInfo::hasPropertyByName( const OUString& rName )
+{
+ return maMap.find ( rName ) != maMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/genericpropertyset.cxx b/comphelper/source/property/genericpropertyset.cxx
new file mode 100644
index 000000000..b459de64c
--- /dev/null
+++ b/comphelper/source/property/genericpropertyset.cxx
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <map>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <cppuhelper/weakagg.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/multiinterfacecontainer4.hxx>
+#include <comphelper/propertysethelper.hxx>
+#include <mutex>
+#include <rtl/ref.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/propertysetinfo.hxx>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+namespace comphelper
+{
+ namespace {
+
+ class GenericPropertySet : public OWeakAggObject,
+ public XServiceInfo,
+ public XTypeProvider,
+ public PropertySetHelper
+ {
+ private:
+ std::map<OUString, Any> maAnyMap;
+ std::mutex maMutex;
+ comphelper::OMultiTypeInterfaceContainerHelperVar4<OUString, XPropertyChangeListener> m_aListener;
+
+ protected:
+ virtual void _setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues ) override;
+ virtual void _getPropertyValues( const PropertyMapEntry** ppEntries, Any* pValue ) override;
+
+ public:
+ explicit GenericPropertySet( PropertySetInfo* pInfo ) noexcept;
+
+ // XInterface
+ virtual Any SAL_CALL queryAggregation( const Type & rType ) override;
+ virtual Any SAL_CALL queryInterface( const Type & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+ virtual Sequence< Type > SAL_CALL getTypes( ) override;
+ virtual Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ };
+
+ }
+}
+
+
+GenericPropertySet::GenericPropertySet( PropertySetInfo* pInfo ) noexcept
+: PropertySetHelper( pInfo )
+{
+}
+
+void SAL_CALL GenericPropertySet::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+{
+ Reference < XPropertySetInfo > xInfo = getPropertySetInfo( );
+ if ( !xInfo.is() )
+ return;
+
+ std::unique_lock aGuard(maMutex);
+ if ( aPropertyName.isEmpty() )
+ {
+ Sequence< Property> aSeq = xInfo->getProperties();
+ const Property* pIter = aSeq.getConstArray();
+ const Property* pEnd = pIter + aSeq.getLength();
+ for( ; pIter != pEnd ; ++pIter)
+ {
+ m_aListener.addInterface(aGuard, pIter->Name,xListener);
+ }
+ }
+ else if ( xInfo->hasPropertyByName(aPropertyName) )
+ m_aListener.addInterface(aGuard, aPropertyName,xListener);
+ else
+ throw UnknownPropertyException( aPropertyName, *this );
+}
+
+void SAL_CALL GenericPropertySet::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener )
+{
+ Reference < XPropertySetInfo > xInfo = getPropertySetInfo( );
+ if ( !xInfo.is() )
+ return;
+
+ std::unique_lock aGuard(maMutex);
+ if ( aPropertyName.isEmpty() )
+ {
+ Sequence< Property> aSeq = xInfo->getProperties();
+ const Property* pIter = aSeq.getConstArray();
+ const Property* pEnd = pIter + aSeq.getLength();
+ for( ; pIter != pEnd ; ++pIter)
+ {
+ m_aListener.removeInterface(aGuard, pIter->Name,xListener);
+ }
+ }
+ else if ( xInfo->hasPropertyByName(aPropertyName) )
+ m_aListener.removeInterface(aGuard, aPropertyName,xListener);
+ else
+ throw UnknownPropertyException( aPropertyName, *this );
+}
+
+void GenericPropertySet::_setPropertyValues( const PropertyMapEntry** ppEntries, const Any* pValues )
+{
+ std::unique_lock aGuard(maMutex);
+
+ while( *ppEntries )
+ {
+ OInterfaceContainerHelper4<XPropertyChangeListener> * pHelper = m_aListener.getContainer((*ppEntries)->maName);
+
+ maAnyMap[ (*ppEntries)->maName ] = *pValues;
+
+ if ( pHelper )
+ {
+ PropertyChangeEvent aEvt;
+ aEvt.PropertyName = (*ppEntries)->maName;
+ aEvt.NewValue = *pValues;
+ pHelper->notifyEach( aGuard, &XPropertyChangeListener::propertyChange, aEvt );
+ aGuard.lock();
+ }
+
+ ppEntries++;
+ pValues++;
+ }
+}
+
+void GenericPropertySet::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, Any* pValue )
+{
+ std::unique_lock aGuard(maMutex);
+
+ while( *ppEntries )
+ {
+ *pValue = maAnyMap[ (*ppEntries)->maName ];
+
+ ppEntries++;
+ pValue++;
+ }
+}
+
+// XInterface
+
+Any SAL_CALL GenericPropertySet::queryInterface( const Type & rType )
+{
+ return OWeakAggObject::queryInterface( rType );
+}
+
+Any SAL_CALL GenericPropertySet::queryAggregation( const Type & rType )
+{
+ Any aAny;
+
+ if( rType == cppu::UnoType<XServiceInfo>::get())
+ aAny <<= Reference< XServiceInfo >(this);
+ else if( rType == cppu::UnoType<XTypeProvider>::get())
+ aAny <<= Reference< XTypeProvider >(this);
+ else if( rType == cppu::UnoType<XPropertySet>::get())
+ aAny <<= Reference< XPropertySet >(this);
+ else if( rType == cppu::UnoType<XMultiPropertySet>::get())
+ aAny <<= Reference< XMultiPropertySet >(this);
+ else
+ aAny = OWeakAggObject::queryAggregation( rType );
+
+ return aAny;
+}
+
+void SAL_CALL GenericPropertySet::acquire() noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+void SAL_CALL GenericPropertySet::release() noexcept
+{
+ OWeakAggObject::release();
+}
+
+uno::Sequence< uno::Type > SAL_CALL GenericPropertySet::getTypes()
+{
+ return uno::Sequence {
+ cppu::UnoType<XAggregation>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get() };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GenericPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL GenericPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL GenericPropertySet::getImplementationName()
+{
+ return "com.sun.star.comp.comphelper.GenericPropertySet";
+}
+
+Sequence< OUString > SAL_CALL GenericPropertySet::getSupportedServiceNames( )
+{
+ return { "com.sun.star.beans.XPropertySet" };
+}
+
+css::uno::Reference< css::beans::XPropertySet > comphelper::GenericPropertySet_CreateInstance( comphelper::PropertySetInfo* pInfo )
+{
+ return static_cast<XPropertySet*>(new GenericPropertySet( pInfo ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/opropertybag.cxx b/comphelper/source/property/opropertybag.cxx
new file mode 100644
index 000000000..e0b389c19
--- /dev/null
+++ b/comphelper/source/property/opropertybag.cxx
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include "opropertybag.hxx"
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/Property.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <algorithm>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+using namespace ::com::sun::star;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_comphelper_OPropertyBag (
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new comphelper::OPropertyBag());
+}
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::container;
+
+ OPropertyBag::OPropertyBag()
+ :OPropertyBag_PBase( GetBroadcastHelper(), this )
+ ,::cppu::IEventNotificationHook()
+ ,m_bAutoAddProperties( false )
+ ,m_NotifyListeners(m_aMutex)
+ ,m_isModified(false)
+
+ {
+ }
+
+
+ OPropertyBag::~OPropertyBag()
+ {
+ }
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase )
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyBag, OPropertyBag_Base, OPropertyBag_PBase )
+
+ void SAL_CALL OPropertyBag::initialize( const Sequence< Any >& _rArguments )
+ {
+ Sequence< Type > aTypes;
+ bool AllowEmptyPropertyName(false);
+ bool AutomaticAddition(false);
+
+ if (_rArguments.getLength() == 3
+ && (_rArguments[0] >>= aTypes)
+ && (_rArguments[1] >>= AllowEmptyPropertyName)
+ && (_rArguments[2] >>= AutomaticAddition))
+ {
+ m_aAllowedTypes.insert(std::cbegin(aTypes), std::cend(aTypes));
+ m_bAutoAddProperties = AutomaticAddition;
+
+ } else {
+ ::comphelper::NamedValueCollection aArguments( _rArguments );
+
+ if ( aArguments.get_ensureType( "AllowedTypes", aTypes ) )
+ m_aAllowedTypes.insert(std::cbegin(aTypes), std::cend(aTypes));
+
+ aArguments.get_ensureType( "AutomaticAddition", m_bAutoAddProperties );
+ aArguments.get_ensureType( "AllowEmptyPropertyName",
+ AllowEmptyPropertyName );
+ }
+ if (AllowEmptyPropertyName) {
+ m_aDynamicProperties.setAllowEmptyPropertyName(
+ AllowEmptyPropertyName);
+ }
+ }
+
+ OUString SAL_CALL OPropertyBag::getImplementationName()
+ {
+ return "com.sun.star.comp.comphelper.OPropertyBag";
+ }
+
+ sal_Bool SAL_CALL OPropertyBag::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ Sequence< OUString > SAL_CALL OPropertyBag::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.beans.PropertyBag" };
+ }
+
+ void OPropertyBag::fireEvents(
+ sal_Int32 * /*pnHandles*/,
+ sal_Int32 nCount,
+ sal_Bool bVetoable,
+ bool bIgnoreRuntimeExceptionsWhileFiring)
+ {
+ if (nCount && !bVetoable) {
+ setModifiedImpl(true, bIgnoreRuntimeExceptionsWhileFiring);
+ }
+ }
+
+ void OPropertyBag::setModifiedImpl(bool bModified,
+ bool bIgnoreRuntimeExceptionsWhileFiring)
+ {
+ { // do not lock mutex while notifying (#i93514#) to prevent deadlock
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_isModified = bModified;
+ }
+ if (!bModified)
+ return;
+
+ try {
+ Reference<XInterface> xThis(*this);
+ EventObject event(xThis);
+ m_NotifyListeners.notifyEach(
+ &XModifyListener::modified, event);
+ } catch (RuntimeException &) {
+ if (!bIgnoreRuntimeExceptionsWhileFiring) {
+ throw;
+ }
+ } catch (Exception &) {
+ // ignore
+ }
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::isModified()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_isModified;
+ }
+
+ void SAL_CALL OPropertyBag::setModified( sal_Bool bModified )
+ {
+ setModifiedImpl(bModified, false);
+ }
+
+ void SAL_CALL OPropertyBag::addModifyListener(
+ const Reference< XModifyListener > & xListener)
+ {
+ m_NotifyListeners.addInterface(xListener);
+ }
+
+ void SAL_CALL OPropertyBag::removeModifyListener(
+ const Reference< XModifyListener > & xListener)
+ {
+ m_NotifyListeners.removeInterface(xListener);
+ }
+
+
+ Reference< XPropertySetInfo > SAL_CALL OPropertyBag::getPropertySetInfo( )
+ {
+ return createPropertySetInfo( getInfoHelper() );
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::has( const Any& /*aElement*/ )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return false;
+ }
+
+
+ void SAL_CALL OPropertyBag::insert( const Any& _element )
+ {
+ // This is a workaround for addProperty not being able to add default-void properties.
+ // If we ever have a smarter XPropertyContainer::addProperty interface, we can remove this, ehm, well, hack.
+ Property aProperty;
+ if ( !( _element >>= aProperty ) )
+ throw IllegalArgumentException( "element is not Property", *this, 1 );
+
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ // check whether the type is allowed, everything else will be checked
+ // by m_aDynamicProperties
+ if (!m_aAllowedTypes.empty()
+ && m_aAllowedTypes.find(aProperty.Type) == m_aAllowedTypes.end())
+ throw IllegalArgumentException("not in list of allowed types", *this, 1);
+
+ m_aDynamicProperties.addVoidProperty(aProperty.Name, aProperty.Type, findFreeHandle(),
+ aProperty.Attributes);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ void SAL_CALL OPropertyBag::remove( const Any& /*aElement*/ )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ throw NoSuchElementException( OUString(), *this );
+ }
+
+
+ Reference< XEnumeration > SAL_CALL OPropertyBag::createEnumeration( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return nullptr;
+ }
+
+
+ Type SAL_CALL OPropertyBag::getElementType( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return Type();
+ }
+
+
+ sal_Bool SAL_CALL OPropertyBag::hasElements( )
+ {
+ // XSet is only a workaround for addProperty not being able to add default-void properties.
+ // So, everything of XSet except insert is implemented empty
+ return false;
+ }
+
+
+ void SAL_CALL OPropertyBag::getFastPropertyValue( Any& _rValue, sal_Int32 _nHandle ) const
+ {
+ m_aDynamicProperties.getFastPropertyValue( _nHandle, _rValue );
+ }
+
+ sal_Bool SAL_CALL OPropertyBag::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+ {
+ return m_aDynamicProperties.convertFastPropertyValue( _nHandle, _rValue, _rConvertedValue, _rOldValue );
+ }
+
+ void SAL_CALL OPropertyBag::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
+ {
+ m_aDynamicProperties.setFastPropertyValue( nHandle, rValue );
+ }
+
+
+ ::cppu::IPropertyArrayHelper& SAL_CALL OPropertyBag::getInfoHelper()
+ {
+ if (!m_pArrayHelper)
+ {
+ Sequence< Property > aProperties;
+ m_aDynamicProperties.describeProperties( aProperties );
+ m_pArrayHelper.reset( new ::cppu::OPropertyArrayHelper( aProperties ) );
+ }
+ return *m_pArrayHelper;
+
+ }
+
+
+ sal_Int32 OPropertyBag::findFreeHandle() const
+ {
+ const sal_Int32 nPrime = 1009;
+ const sal_Int32 nSeed = 11;
+
+ sal_Int32 nCheck = nSeed;
+ while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) && ( nCheck != 1 ) )
+ {
+ nCheck = ( nCheck * nSeed ) % nPrime;
+ }
+
+ if ( nCheck == 1 )
+ { // uh ... we already have 1008 handles used up
+ // -> simply count upwards
+ while ( m_aDynamicProperties.hasPropertyByHandle( nCheck ) )
+ ++nCheck;
+ }
+
+ return nCheck;
+ }
+
+
+ void SAL_CALL OPropertyBag::addProperty( const OUString& _rName, ::sal_Int16 _nAttributes, const Any& _rInitialValue )
+ {
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ // check whether the type is allowed, everything else will be checked
+ // by m_aDynamicProperties
+ const Type& aPropertyType = _rInitialValue.getValueType();
+ if (_rInitialValue.hasValue() && !m_aAllowedTypes.empty()
+ && m_aAllowedTypes.find(aPropertyType) == m_aAllowedTypes.end())
+ throw IllegalTypeException(OUString(), *this);
+
+ m_aDynamicProperties.addProperty(_rName, findFreeHandle(), _nAttributes,
+ _rInitialValue);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ void SAL_CALL OPropertyBag::removeProperty( const OUString& _rName )
+ {
+ {
+ osl::MutexGuard g(m_aMutex);
+
+ m_aDynamicProperties.removeProperty(_rName);
+
+ // our property info is dirty
+ m_pArrayHelper.reset();
+ }
+ setModified(true);
+ }
+
+
+ namespace
+ {
+ struct ComparePropertyValueByName
+ {
+ bool operator()( const PropertyValue& _rLHS, const PropertyValue& _rRHS )
+ {
+ return _rLHS.Name < _rRHS.Name;
+ }
+ };
+
+ template< typename CLASS >
+ struct TransformPropertyToName
+ {
+ const OUString& operator()( const CLASS& _rProp )
+ {
+ return _rProp.Name;
+ }
+ };
+
+ struct ExtractPropertyValue
+ {
+ const Any& operator()( const PropertyValue& _rProp )
+ {
+ return _rProp.Value;
+ }
+ };
+ }
+
+
+ Sequence< PropertyValue > SAL_CALL OPropertyBag::getPropertyValues( )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // all registered properties
+ Sequence< Property > aProperties;
+ m_aDynamicProperties.describeProperties( aProperties );
+
+ // their names
+ Sequence< OUString > aNames( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aNames.getArray(),
+ TransformPropertyToName< Property >()
+ );
+
+ // their values
+ Sequence< Any > aValues;
+ try
+ {
+ aValues = OPropertyBag_PBase::getPropertyValues( aNames );
+ if ( aValues.getLength() != aNames.getLength() )
+ throw RuntimeException();
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ // ignore
+ }
+
+ // merge names and values, and retrieve the state/handle
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+
+ Sequence< PropertyValue > aPropertyValues( aNames.getLength() );
+ const OUString* pName = aNames.getConstArray();
+ const OUString* pNamesEnd = aNames.getConstArray() + aNames.getLength();
+ const Any* pValue = aValues.getArray();
+ PropertyValue* pPropertyValue = aPropertyValues.getArray();
+
+ for ( ; pName != pNamesEnd; ++pName, ++pValue, ++pPropertyValue )
+ {
+ pPropertyValue->Name = *pName;
+ pPropertyValue->Handle = rPropInfo.getHandleByName( *pName );
+ pPropertyValue->Value = *pValue;
+ pPropertyValue->State = getPropertyStateByHandle( pPropertyValue->Handle );
+ }
+
+ return aPropertyValues;
+ }
+
+
+ void OPropertyBag::impl_setPropertyValues_throw( const Sequence< PropertyValue >& _rProps )
+ {
+ // sort (the XMultiPropertySet interface requires this)
+ Sequence< PropertyValue > aProperties( _rProps );
+ auto [begin, end] = asNonConstRange(aProperties);
+ std::sort(
+ begin,
+ end,
+ ComparePropertyValueByName()
+ );
+
+ // a sequence of names
+ Sequence< OUString > aNames( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aNames.getArray(),
+ TransformPropertyToName< PropertyValue >()
+ );
+
+ try
+ {
+ // check for unknown properties
+ // we cannot simply rely on the XMultiPropertySet::setPropertyValues
+ // implementation of our base class, since it does not throw
+ // an UnknownPropertyException. More precise, XMultiPropertySet::setPropertyValues
+ // does not allow to throw this exception, while XPropertyAccess::setPropertyValues
+ // requires it
+ sal_Int32 nCount = aNames.getLength();
+
+ Sequence< sal_Int32 > aHandles( nCount );
+ sal_Int32* pHandle = aHandles.getArray();
+ const PropertyValue* pProperty = aProperties.getConstArray();
+ for ( const OUString* pName = aNames.getConstArray();
+ pName != aNames.getConstArray() + aNames.getLength();
+ ++pName, ++pHandle, ++pProperty
+ )
+ {
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+ *pHandle = rPropInfo.getHandleByName( *pName );
+ if ( *pHandle != -1 )
+ continue;
+
+ // there's a property requested which we do not know
+ if ( m_bAutoAddProperties )
+ {
+ // add the property
+ sal_Int16 const nAttributes = PropertyAttribute::BOUND | PropertyAttribute::REMOVABLE | PropertyAttribute::MAYBEDEFAULT;
+ addProperty( *pName, nAttributes, pProperty->Value );
+ continue;
+ }
+
+ // no way out
+ throw UnknownPropertyException( *pName, *this );
+ }
+
+ // a sequence of values
+ Sequence< Any > aValues( aProperties.getLength() );
+ std::transform(
+ std::cbegin(aProperties),
+ std::cend(aProperties),
+ aValues.getArray(),
+ ExtractPropertyValue()
+ );
+
+ setFastPropertyValues( nCount, aHandles.getArray(), aValues.getConstArray(), nCount );
+ }
+ catch( const PropertyVetoException& ) { throw; }
+ catch( const IllegalArgumentException& ) { throw; }
+ catch( const WrappedTargetException& ) { throw; }
+ catch( const RuntimeException& ) { throw; }
+ catch( const UnknownPropertyException& ) { throw; }
+ catch( const Exception& )
+ {
+ throw WrappedTargetException( OUString(), *this, ::cppu::getCaughtException() );
+ }
+ }
+
+
+ void SAL_CALL OPropertyBag::setPropertyValues( const Sequence< PropertyValue >& _rProps )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_setPropertyValues_throw( _rProps );
+ }
+
+
+ PropertyState OPropertyBag::getPropertyStateByHandle( sal_Int32 _nHandle )
+ {
+ // for properties which do not support the MAYBEDEFAULT attribute, don't rely on the base class, but
+ // assume they're always in DIRECT state.
+ // (Note that this probably would belong into the base class. However, this would mean we would need
+ // to check all existent usages of the base class, where MAYBEDEFAULT is *not* set, but
+ // a default is nonetheless supplied/used. This is hard to accomplish reliably, in the
+ // current phase. #i78593#
+
+ ::cppu::IPropertyArrayHelper& rPropInfo = getInfoHelper();
+ sal_Int16 nAttributes(0);
+ OSL_VERIFY( rPropInfo.fillPropertyMembersByHandle( nullptr, &nAttributes, _nHandle ) );
+ if ( ( nAttributes & PropertyAttribute::MAYBEDEFAULT ) == 0 )
+ return PropertyState_DIRECT_VALUE;
+
+ return OPropertyBag_PBase::getPropertyStateByHandle( _nHandle );
+ }
+
+
+ Any OPropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle ) const
+ {
+ Any aDefault;
+ m_aDynamicProperties.getPropertyDefaultByHandle( _nHandle, aDefault );
+ return aDefault;
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/opropertybag.hxx b/comphelper/source/property/opropertybag.hxx
new file mode 100644
index 000000000..28333901c
--- /dev/null
+++ b/comphelper/source/property/opropertybag.hxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/beans/XPropertyBag.hpp>
+#include <com/sun/star/container/XSet.hpp>
+
+#include <cppuhelper/implbase5.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/propstate.hxx>
+#include <comphelper/broadcasthelper.hxx>
+#include <comphelper/propertybag.hxx>
+#include <comphelper/uno3.hxx>
+
+#include <map>
+#include <set>
+#include <memory>
+
+
+namespace comphelper
+{
+
+
+ struct UnoTypeLess
+ {
+ bool operator()( const css::uno::Type& _rLHS, const css::uno::Type& _rRHS ) const
+ {
+ return rtl_ustr_compare(
+ _rLHS.getTypeLibType()->pTypeName->buffer,
+ _rRHS.getTypeLibType()->pTypeName->buffer
+ ) < 0;
+ }
+ };
+
+ typedef std::map< sal_Int32, css::uno::Any > MapInt2Any;
+ typedef std::set< css::uno::Type, UnoTypeLess > TypeBag;
+
+ typedef ::cppu::WeakAggImplHelper5 < css::beans::XPropertyBag
+ , css::util::XModifiable
+ , css::lang::XServiceInfo
+ , css::lang::XInitialization
+ , css::container::XSet
+ > OPropertyBag_Base;
+ typedef ::comphelper::OPropertyStateHelper OPropertyBag_PBase;
+
+ class OPropertyBag final : public ::comphelper::OMutexAndBroadcastHelper // must be before OPropertyBag_PBase
+ ,public OPropertyBag_PBase
+ ,public OPropertyBag_Base
+ ,public ::cppu::IEventNotificationHook
+ {
+ private:
+ /// our IPropertyArrayHelper implementation
+ std::unique_ptr< ::cppu::OPropertyArrayHelper >
+ m_pArrayHelper;
+ ::comphelper::PropertyBag
+ m_aDynamicProperties;
+ /// set of allowed property types
+ TypeBag m_aAllowedTypes;
+ /// should we automatically add properties which are tried to set, if they don't exist previously?
+ bool m_bAutoAddProperties;
+
+ /// for notification
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener> m_NotifyListeners;
+ /// modify flag
+ bool m_isModified;
+
+ public:
+ //noncopyable
+ OPropertyBag(const OPropertyBag&) = delete;
+ const OPropertyBag& operator=(const OPropertyBag&) = delete;
+ OPropertyBag();
+ virtual ~OPropertyBag() override;
+
+ private:
+ DECLARE_XINTERFACE()
+ DECLARE_XTYPEPROVIDER()
+
+ /** === begin UNO interface implementations == **/
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XModifiable:
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ const css::uno::Reference<
+ css::util::XModifyListener > & xListener) override;
+ virtual void SAL_CALL removeModifyListener(
+ const css::uno::Reference<
+ css::util::XModifyListener > & xListener) override;
+
+ // XPropertyContainer
+ virtual void SAL_CALL addProperty( const OUString& Name, ::sal_Int16 Attributes, const css::uno::Any& DefaultValue ) override;
+ virtual void SAL_CALL removeProperty( const OUString& Name ) override;
+
+ // XPropertyAccess
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPropertyValues( ) override;
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override
+ { OPropertyBag_PBase::setPropertyValue(p1, p2); }
+ virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override
+ { return OPropertyBag_PBase::getPropertyValue(p1); }
+ virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { OPropertyBag_PBase::addPropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XPropertyChangeListener>& p2) override
+ { OPropertyBag_PBase::removePropertyChangeListener(p1, p2); }
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { OPropertyBag_PBase::addVetoableChangeListener(p1, p2); }
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference<css::beans::XVetoableChangeListener>& p2) override
+ { OPropertyBag_PBase::removeVetoableChangeListener(p1, p2); }
+
+ // XSet
+ virtual sal_Bool SAL_CALL has( const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL insert( const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL remove( const css::uno::Any& aElement ) override;
+
+ // XEnumerationAccess (base of XSet)
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createEnumeration( ) override;
+
+ // XElementAccess (base of XEnumerationAccess)
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+ // UNO interface implementations
+
+ // XPropertyState
+ virtual css::uno::Any getPropertyDefaultByHandle( sal_Int32 _nHandle ) const override;
+
+ // OPropertyStateHelper
+ virtual css::beans::PropertyState getPropertyStateByHandle( sal_Int32 _nHandle ) override;
+
+ // OPropertySetHelper
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue, sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // IEventNotificationHook
+ virtual void fireEvents(
+ sal_Int32 * pnHandles,
+ sal_Int32 nCount,
+ sal_Bool bVetoable,
+ bool bIgnoreRuntimeExceptionsWhileFiring) override;
+
+ void setModifiedImpl( bool bModified,
+ bool bIgnoreRuntimeExceptionsWhileFiring);
+
+ /** finds a free property handle
+ @precond
+ our mutex is locked
+ */
+ sal_Int32 findFreeHandle() const;
+
+ /** implements the setPropertyValues method
+ @param _rProps
+ the property values to set
+
+ @throws PropertyVetoException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::lang::IllegalArgumentException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::lang::WrappedTargetException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::uno::RuntimeException
+ if the XMultiPropertySet::setPropertyValues call does so
+
+ @throws css::beans::UnknownPropertyException
+ if the XMultiPropertySet::setPropertyValues call does so, and <arg>_bTolerateUnknownProperties</arg>
+ was set to <FALSE/>
+
+ @throws css::lang::WrappedTargetException
+ if the XMultiPropertySet::setPropertyValues call did throw an exception not listed
+ above
+ */
+ void impl_setPropertyValues_throw( const css::uno::Sequence< css::beans::PropertyValue >& _rProps );
+
+ using ::cppu::OPropertySetHelper::getPropertyValues;
+ using ::cppu::OPropertySetHelper::setPropertyValues;
+ using ::cppu::OPropertySetHelper::getFastPropertyValue;
+ };
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propagg.cxx b/comphelper/source/property/propagg.cxx
new file mode 100644
index 000000000..0d8ea6a10
--- /dev/null
+++ b/comphelper/source/property/propagg.cxx
@@ -0,0 +1,870 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propagg.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <o3tl/sorted_vector.hxx>
+#include <typeinfo>
+#include <algorithm>
+#include <cstddef>
+#include <unordered_set>
+#include <memory>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+
+ using namespace internal;
+
+
+ namespace
+ {
+ const Property* lcl_findPropertyByName( const std::vector< Property >& _rProps, const OUString& _rName )
+ {
+ Property aNameProp(_rName, 0, Type(), 0);
+ auto pResult = std::lower_bound(_rProps.begin(), _rProps.end(), aNameProp, PropertyCompareByName());
+ if ( pResult == _rProps.end() || pResult->Name != _rName )
+ return nullptr;
+
+ return &*pResult;
+ }
+ }
+
+OPropertyArrayAggregationHelper::OPropertyArrayAggregationHelper(
+ const Sequence< Property >& _rProperties, const Sequence< Property >& _rAggProperties,
+ IPropertyInfoService* _pInfoService, sal_Int32 _nFirstAggregateId )
+{
+ // if properties are present both at the delegatee and the aggregate, then the former are supposed to win
+ // merge and sort properties by name, delete duplicates (stable sort ensures delegator properties win)
+ m_aProperties.insert( m_aProperties.end(), _rProperties.begin(), _rProperties.end() );
+ m_aProperties.insert( m_aProperties.end(), _rAggProperties.begin(), _rAggProperties.end() );
+ std::stable_sort( m_aProperties.begin(), m_aProperties.end(), PropertyCompareByName() );
+ m_aProperties.erase( std::unique(m_aProperties.begin(), m_aProperties.end(),
+ []( const css::beans::Property& x, const css::beans::Property& y ) -> bool { return x.Name == y.Name; } ),
+ m_aProperties.end() );
+ m_aProperties.shrink_to_fit();
+
+ // fill aDelegatorProps with names from _rProperties for a fast existence check
+ // different kinds of properties are processed differently
+ std::unordered_set< OUString > aDelegatorProps;
+ aDelegatorProps.reserve( _rProperties.getLength() );
+ for( auto &delegateProp: _rProperties )
+ {
+ const auto inserted = aDelegatorProps.insert( delegateProp.Name );
+ OSL_ENSURE( inserted.second,
+ "OPropertyArrayAggregationHelper::OPropertyArrayAggregationHelper: duplicate delegatee property!" );
+ }
+
+ std::unordered_set< sal_Int32 > existingHandles;
+ existingHandles.reserve( m_aProperties.size() );
+ sal_Int32 nAggregateHandle = _nFirstAggregateId;
+ for ( std::size_t nMPLoop = 0; nMPLoop < m_aProperties.size(); ++nMPLoop )
+ {
+ auto &prop = m_aProperties[ nMPLoop ];
+ if ( aDelegatorProps.find( prop.Name ) != aDelegatorProps.end() )
+ {
+ m_aPropertyAccessors.insert_or_assign(
+ prop.Handle, OPropertyAccessor( -1, nMPLoop, false ));
+ existingHandles.insert( prop.Handle );
+ }
+ else
+ {
+ // determine the handle for the property which we will expose to the outside world
+ sal_Int32 nHandle = -1;
+ // ask the info service first
+ if ( _pInfoService )
+ nHandle = _pInfoService->getPreferredPropertyId( prop.Name );
+
+ if ( ( -1 == nHandle ) || ( existingHandles.find( nHandle ) != existingHandles.end() ) )
+ {
+ // 1. no handle from the info service -> default
+ // 2. conflicts -> use another one (which we don't check anymore, assuming _nFirstAggregateId was large enough)
+ nHandle = nAggregateHandle++;
+ }
+ else
+ {
+ existingHandles.insert( nHandle );
+ }
+
+ // remember the accessor for this property
+ m_aPropertyAccessors.insert_or_assign(
+ nHandle, OPropertyAccessor( prop.Handle, nMPLoop, true ));
+ prop.Handle = nHandle;
+ }
+ }
+}
+
+
+OPropertyArrayAggregationHelper::PropertyOrigin OPropertyArrayAggregationHelper::classifyProperty( const OUString& _rName )
+{
+ PropertyOrigin eOrigin = PropertyOrigin::Unknown;
+ // look up the name
+ const Property* pPropertyDescriptor = lcl_findPropertyByName( m_aProperties, _rName );
+ if ( pPropertyDescriptor )
+ {
+ // look up the handle for this name
+ auto aPos = m_aPropertyAccessors.find( pPropertyDescriptor->Handle );
+ OSL_ENSURE( m_aPropertyAccessors.end() != aPos, "OPropertyArrayAggregationHelper::classifyProperty: should have this handle in my map!" );
+ if ( m_aPropertyAccessors.end() != aPos )
+ {
+ eOrigin = aPos->second.bAggregate ? PropertyOrigin::Aggregate : PropertyOrigin::Delegator;
+ }
+ }
+ return eOrigin;
+}
+
+
+Property OPropertyArrayAggregationHelper::getPropertyByName( const OUString& _rPropertyName )
+{
+ const Property* pProperty = findPropertyByName( _rPropertyName );
+
+ if ( !pProperty )
+ throw UnknownPropertyException(_rPropertyName);
+
+ return *pProperty;
+}
+
+
+sal_Bool OPropertyArrayAggregationHelper::hasPropertyByName(const OUString& _rPropertyName)
+{
+ return nullptr != findPropertyByName( _rPropertyName );
+}
+
+
+const Property* OPropertyArrayAggregationHelper::findPropertyByName(const OUString& _rName ) const
+{
+ return lcl_findPropertyByName( m_aProperties, _rName );
+}
+
+
+sal_Int32 OPropertyArrayAggregationHelper::getHandleByName(const OUString& _rPropertyName)
+{
+ const Property* pProperty = findPropertyByName( _rPropertyName );
+ return pProperty ? pProperty->Handle : -1;
+}
+
+
+sal_Bool OPropertyArrayAggregationHelper::fillPropertyMembersByHandle(
+ OUString* _pPropName, sal_Int16* _pAttributes, sal_Int32 _nHandle)
+{
+ auto i = m_aPropertyAccessors.find(_nHandle);
+ bool bRet = i != m_aPropertyAccessors.end();
+ if (bRet)
+ {
+ const css::beans::Property& rProperty = m_aProperties[(*i).second.nPos];
+ if (_pPropName)
+ *_pPropName = rProperty.Name;
+ if (_pAttributes)
+ *_pAttributes = rProperty.Attributes;
+ }
+ return bRet;
+}
+
+
+bool OPropertyArrayAggregationHelper::getPropertyByHandle( sal_Int32 _nHandle, Property& _rProperty ) const
+{
+ auto pos = m_aPropertyAccessors.find(_nHandle);
+ if ( pos != m_aPropertyAccessors.end() )
+ {
+ _rProperty = m_aProperties[ pos->second.nPos ];
+ return true;
+ }
+ return false;
+}
+
+
+bool OPropertyArrayAggregationHelper::fillAggregatePropertyInfoByHandle(
+ OUString* _pPropName, sal_Int32* _pOriginalHandle, sal_Int32 _nHandle) const
+{
+ auto i = m_aPropertyAccessors.find(_nHandle);
+ bool bRet = i != m_aPropertyAccessors.end() && (*i).second.bAggregate;
+ if (bRet)
+ {
+ if (_pOriginalHandle)
+ *_pOriginalHandle = (*i).second.nOriginalHandle;
+ if (_pPropName)
+ {
+ OSL_ENSURE((*i).second.nPos < m_aProperties.size(),"Invalid index for sequence!");
+ const css::beans::Property& rProperty = m_aProperties[(*i).second.nPos];
+ *_pPropName = rProperty.Name;
+ }
+ }
+ return bRet;
+}
+
+
+css::uno::Sequence< css::beans::Property> OPropertyArrayAggregationHelper::getProperties()
+{
+ return comphelper::containerToSequence(m_aProperties);
+}
+
+
+sal_Int32 OPropertyArrayAggregationHelper::fillHandles(
+ sal_Int32* _pHandles, const css::uno::Sequence< OUString >& _rPropNames )
+{
+ sal_Int32 nHitCount = 0;
+ const OUString* pReqProps = _rPropNames.getConstArray();
+ sal_Int32 nReqLen = _rPropNames.getLength();
+
+ Property aNameProp;
+ for( sal_Int32 i = 0; i < nReqLen; ++i )
+ {
+ aNameProp.Name = pReqProps[i];
+ auto findIter = std::lower_bound(m_aProperties.begin(), m_aProperties.end(), aNameProp, PropertyCompareByName());
+ if ( findIter != m_aProperties.end() && findIter->Name == pReqProps[i] )
+ {
+ _pHandles[i] = findIter->Handle;
+ nHitCount++;
+ }
+ }
+ return nHitCount;
+}
+
+namespace internal
+{
+ class PropertyForwarder
+ {
+ private:
+ OPropertySetAggregationHelper& m_rAggregationHelper;
+ o3tl::sorted_vector< sal_Int32 > m_aProperties;
+ sal_Int32 m_nCurrentlyForwarding;
+
+ public:
+ explicit PropertyForwarder( OPropertySetAggregationHelper& _rAggregationHelper );
+
+ /** declares that the forwarder should be responsible for the given property
+
+ @param _nHandle
+ the public handle (<em>not</em> the original handle!) of the property
+ */
+ void takeResponsibilityFor( sal_Int32 _nHandle );
+
+ /** checks whether the forwarder is responsible for the given property
+ */
+ bool isResponsibleFor( sal_Int32 _nHandle ) const;
+
+ /// actually forwards a property value to the aggregate
+ ///
+ /// @throws Exception
+ void doForward( sal_Int32 _nHandle, const Any& _rValue );
+
+ sal_Int32 getCurrentlyForwardedProperty( ) const { return m_nCurrentlyForwarding; }
+ };
+
+
+ PropertyForwarder::PropertyForwarder( OPropertySetAggregationHelper& _rAggregationHelper )
+ :m_rAggregationHelper( _rAggregationHelper )
+ ,m_nCurrentlyForwarding( -1 )
+ {
+ }
+
+
+ void PropertyForwarder::takeResponsibilityFor( sal_Int32 _nHandle )
+ {
+ m_aProperties.insert( _nHandle );
+ }
+
+
+ bool PropertyForwarder::isResponsibleFor( sal_Int32 _nHandle ) const
+ {
+ return m_aProperties.find( _nHandle ) != m_aProperties.end();
+ }
+
+
+ void PropertyForwarder::doForward( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ OSL_ENSURE( m_rAggregationHelper.m_xAggregateSet.is(), "PropertyForwarder::doForward: no property set!" );
+ if ( !m_rAggregationHelper.m_xAggregateSet.is() )
+ return;
+
+ m_rAggregationHelper.forwardingPropertyValue( _nHandle );
+
+ OSL_ENSURE( m_nCurrentlyForwarding == -1, "PropertyForwarder::doForward: reentrance?" );
+ m_nCurrentlyForwarding = _nHandle;
+
+ try
+ {
+ m_rAggregationHelper.m_xAggregateSet->setPropertyValue( m_rAggregationHelper.getPropertyName( _nHandle ), _rValue );
+ // TODO: cache the property name? (it's a O(log n) search)
+ }
+ catch( const Exception& )
+ {
+ m_rAggregationHelper.forwardedPropertyValue( _nHandle );
+ throw;
+ }
+
+ m_nCurrentlyForwarding = -1;
+
+ m_rAggregationHelper.forwardedPropertyValue( _nHandle );
+ }
+}
+
+OPropertySetAggregationHelper::OPropertySetAggregationHelper( ::cppu::OBroadcastHelper& rBHlp )
+ :OPropertyStateHelper( rBHlp )
+ ,m_bListening( false )
+{
+ m_pForwarder.reset( new PropertyForwarder( *this ) );
+}
+
+
+OPropertySetAggregationHelper::~OPropertySetAggregationHelper()
+{
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::queryInterface(const css::uno::Type& _rType)
+{
+ css::uno::Any aReturn = OPropertyStateHelper::queryInterface(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = cppu::queryInterface(_rType
+ ,static_cast< css::beans::XPropertiesChangeListener*>(this)
+ ,static_cast< css::beans::XVetoableChangeListener*>(this)
+ ,static_cast< css::lang::XEventListener*>(static_cast< css::beans::XPropertiesChangeListener*>(this))
+ );
+
+ return aReturn;
+}
+
+
+void OPropertySetAggregationHelper::disposing()
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if ( m_xAggregateSet.is() && m_bListening )
+ {
+ // register as a single listener
+ m_xAggregateMultiSet->removePropertiesChangeListener(this);
+ m_xAggregateSet->removeVetoableChangeListener(OUString(), this);
+ m_bListening = false;
+ }
+
+ OPropertyStateHelper::disposing();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::disposing(const css::lang::EventObject& _rSource)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::disposing : don't have an aggregate anymore !");
+ if (_rSource.Source == m_xAggregateSet)
+ m_bListening = false;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::propertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent>& _rEvents)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::propertiesChange : have no aggregate !");
+
+ sal_Int32 nLen = _rEvents.getLength();
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+
+ if (1 == nLen)
+ {
+ const css::beans::PropertyChangeEvent& evt = _rEvents.getConstArray()[0];
+ OSL_ENSURE(!evt.PropertyName.isEmpty(), "OPropertySetAggregationHelper::propertiesChange : invalid event !");
+ // we had a bug where this assertion would have us saved a whole day :) (72514)
+ sal_Int32 nHandle = rPH.getHandleByName( evt.PropertyName );
+
+ // If nHandle is -1 the event marks a (aggregate) property which we hide to callers
+ // If isCurrentlyForwardingProperty( nHandle ) is <TRUE/>, then we ourself triggered
+ // setting this property. In this case, it will be notified later (by the OPropertySetHelper
+ // implementation)
+
+ if ( ( nHandle != -1 ) && !isCurrentlyForwardingProperty( nHandle ) )
+ fire(&nHandle, &evt.NewValue, &evt.OldValue, 1, false);
+ }
+ else
+ {
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nLen]);
+ std::unique_ptr< css::uno::Any[]> pNewValues(new css::uno::Any[nLen]);
+ std::unique_ptr< css::uno::Any[]> pOldValues(new css::uno::Any[nLen]);
+
+ sal_Int32 nDest = 0;
+ for (const css::beans::PropertyChangeEvent& rEvent : _rEvents)
+ {
+ sal_Int32 nHandle = rPH.getHandleByName(rEvent.PropertyName);
+ if ( ( nHandle != -1 ) && !isCurrentlyForwardingProperty( nHandle ) )
+ { // same as above : -1 is valid (73247) ...
+ pHandles[nDest] = nHandle;
+ pNewValues[nDest] = rEvent.NewValue;
+ pOldValues[nDest] = rEvent.OldValue;
+ ++nDest;
+ }
+ }
+
+ if (nDest)
+ fire(pHandles.get(), pNewValues.get(), pOldValues.get(), nDest, false);
+ }
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::vetoableChange(const css::beans::PropertyChangeEvent& _rEvent)
+{
+ OSL_ENSURE(m_xAggregateSet.is(), "OPropertySetAggregationHelper::vetoableChange : have no aggregate !");
+
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+
+ sal_Int32 nHandle = rPH.getHandleByName(_rEvent.PropertyName);
+ fire(&nHandle, &_rEvent.NewValue, &_rEvent.OldValue, 1, true);
+}
+
+
+void OPropertySetAggregationHelper::setAggregation(const css::uno::Reference< css::uno::XInterface >& _rxDelegate)
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if (m_bListening && m_xAggregateSet.is())
+ {
+ m_xAggregateMultiSet->removePropertiesChangeListener(this);
+ m_xAggregateSet->removeVetoableChangeListener(OUString(), this);
+ m_bListening = false;
+ }
+
+ m_xAggregateState.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateSet.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateMultiSet.set(_rxDelegate, css::uno::UNO_QUERY);
+ m_xAggregateFastSet.set(_rxDelegate, css::uno::UNO_QUERY);
+
+ // must support XPropertySet and XMultiPropertySet
+ if ( m_xAggregateSet.is() && !m_xAggregateMultiSet.is() )
+ throw css::lang::IllegalArgumentException();
+}
+
+
+void OPropertySetAggregationHelper::startListening()
+{
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+
+ if (!m_bListening && m_xAggregateSet.is())
+ {
+ // register as a single listener
+ css::uno::Sequence< OUString > aPropertyNames;
+ m_xAggregateMultiSet->addPropertiesChangeListener(aPropertyNames, this);
+ m_xAggregateSet->addVetoableChangeListener(OUString(), this);
+
+ m_bListening = true;
+ }
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addVetoableChangeListener(const OUString& _rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addVetoableChangeListener(_rPropertyName, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addPropertyChangeListener(const OUString& _rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addPropertyChangeListener(_rPropertyName, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::addPropertiesChangeListener(const css::uno::Sequence< OUString >& _rPropertyNames,
+ const css::uno::Reference< css::beans::XPropertiesChangeListener>& _rxListener)
+{
+ OPropertySetHelper::addPropertiesChangeListener(_rPropertyNames, _rxListener);
+ if (!m_bListening)
+ startListening();
+}
+
+
+sal_Int32 OPropertySetAggregationHelper::getOriginalHandle(sal_Int32 nHandle) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ sal_Int32 nOriginalHandle = -1;
+ (void)rPH.fillAggregatePropertyInfoByHandle(nullptr, &nOriginalHandle, nHandle);
+ return nOriginalHandle;
+}
+
+
+OUString OPropertySetAggregationHelper::getPropertyName( sal_Int32 _nHandle ) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ Property aProperty;
+ OSL_VERIFY( rPH.getPropertyByHandle( _nHandle, aProperty ) );
+ return aProperty.Name;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setFastPropertyValue(sal_Int32 _nHandle, const css::uno::Any& _rValue)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+
+ // does the handle belong to the aggregation ?
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, _nHandle))
+ if (m_xAggregateFastSet.is())
+ m_xAggregateFastSet->setFastPropertyValue(nOriginalHandle, _rValue);
+ else
+ m_xAggregateSet->setPropertyValue(aPropName, _rValue);
+ else
+ OPropertySetHelper::setFastPropertyValue(_nHandle, _rValue);
+}
+
+
+void OPropertySetAggregationHelper::getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle) const
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast<OPropertyArrayAggregationHelper&>( const_cast<OPropertySetAggregationHelper*>(this)->getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateFastSet.is())
+ rValue = m_xAggregateFastSet->getFastPropertyValue(nOriginalHandle);
+ else
+ rValue = m_xAggregateSet->getPropertyValue(aPropName);
+ }
+ else if ( m_pForwarder->isResponsibleFor( nHandle ) )
+ {
+ // this is a property which has been "overwritten" in our instance (thus
+ // fillAggregatePropertyInfoByHandle didn't find it)
+ rValue = m_xAggregateSet->getPropertyValue( getPropertyName( nHandle ) );
+ }
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::getFastPropertyValue(sal_Int32 nHandle)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ css::uno::Any aValue;
+
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateFastSet.is())
+ aValue = m_xAggregateFastSet->getFastPropertyValue(nOriginalHandle);
+ else
+ aValue = m_xAggregateSet->getPropertyValue(aPropName);
+ }
+ else
+ aValue = OPropertySetHelper::getFastPropertyValue(nHandle);
+
+ return aValue;
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setPropertyValues(
+ const Sequence< OUString >& _rPropertyNames, const Sequence< Any >& _rValues )
+{
+ OSL_ENSURE( !rBHelper.bInDispose, "OPropertySetAggregationHelper::setPropertyValues : do not use within the dispose call !");
+ OSL_ENSURE( !rBHelper.bDisposed, "OPropertySetAggregationHelper::setPropertyValues : object is disposed" );
+
+ // check where the properties come from
+ if (!m_xAggregateSet.is())
+ OPropertySetHelper::setPropertyValues(_rPropertyNames, _rValues);
+ else if (_rPropertyNames.getLength() == 1) // use the more efficient way
+ {
+ try
+ {
+ setPropertyValue( _rPropertyNames[0], _rValues[0] );
+ }
+ catch( const UnknownPropertyException& )
+ {
+ // by definition of XMultiPropertySet::setPropertyValues, unknown properties are to be ignored
+ SAL_WARN( "comphelper", "OPropertySetAggregationHelper::setPropertyValues: unknown property: '"
+ << _rPropertyNames[0] << "', implementation: " << typeid( *this ).name() );
+ }
+ }
+ else
+ {
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+
+ // determine which properties belong to the aggregate, and which ones to the delegator
+ sal_Int32 nAggCount(0);
+ sal_Int32 nLen(_rPropertyNames.getLength());
+
+ for ( const OUString& rName : _rPropertyNames )
+ {
+ OPropertyArrayAggregationHelper::PropertyOrigin ePropOrg = rPH.classifyProperty( rName );
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Unknown == ePropOrg )
+ throw WrappedTargetException( OUString(), static_cast< XMultiPropertySet* >( this ), Any( UnknownPropertyException( ) ) );
+ // due to a flaw in the API design, this method is not allowed to throw an UnknownPropertyException
+ // so we wrap it into a WrappedTargetException
+
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Aggregate == ePropOrg )
+ ++nAggCount;
+ }
+
+ // all properties belong to the aggregate
+ if (nAggCount == nLen)
+ m_xAggregateMultiSet->setPropertyValues(_rPropertyNames, _rValues);
+
+ // all properties belong to the aggregating object
+ else if (nAggCount == 0)
+ OPropertySetHelper::setPropertyValues(_rPropertyNames, _rValues);
+
+ // mixed
+ else
+ {
+ const css::uno::Any* pValues = _rValues.getConstArray();
+
+ // dividing the Names and _rValues
+
+ // aggregate's names
+ Sequence< OUString > AggPropertyNames( nAggCount );
+ OUString* pAggNames = AggPropertyNames.getArray();
+ // aggregate's values
+ Sequence< Any > AggValues( nAggCount );
+ Any* pAggValues = AggValues.getArray();
+
+ // delegator names
+ Sequence< OUString > DelPropertyNames( nLen - nAggCount );
+ OUString* pDelNames = DelPropertyNames.getArray();
+
+ // delegator values
+ Sequence< Any > DelValues( nLen - nAggCount );
+ Any* pDelValues = DelValues.getArray();
+
+ for ( const OUString& rName : _rPropertyNames )
+ {
+ if ( OPropertyArrayAggregationHelper::PropertyOrigin::Aggregate == rPH.classifyProperty( rName ) )
+ {
+ *pAggNames++ = rName;
+ *pAggValues++ = *pValues++;
+ }
+ else
+ {
+ *pDelNames++ = rName;
+ *pDelValues++ = *pValues++;
+ }
+ }
+
+ // reset, needed below
+ pDelValues = DelValues.getArray();
+
+ std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ nLen - nAggCount ]);
+
+ // get the map table
+ cppu::IPropertyArrayHelper& rPH2 = getInfoHelper();
+
+ // fill the handle array
+ sal_Int32 nHitCount = rPH2.fillHandles( pHandles.get(), DelPropertyNames );
+ if (nHitCount != 0)
+ {
+ std::unique_ptr< css::uno::Any[]> pConvertedValues(new css::uno::Any[ nHitCount ]);
+ std::unique_ptr< css::uno::Any[]> pOldValues(new css::uno::Any[ nHitCount ]);
+ nHitCount = 0;
+ sal_Int32 i;
+
+ {
+ // must lock the mutex outside the loop. So all values are consistent.
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ for( i = 0; i < (nLen - nAggCount); ++i )
+ {
+ if( pHandles[i] != -1 )
+ {
+ sal_Int16 nAttributes;
+ rPH2.fillPropertyMembersByHandle( nullptr, &nAttributes, pHandles[i] );
+ if( nAttributes & css::beans::PropertyAttribute::READONLY )
+ throw css::beans::PropertyVetoException();
+ // Will the property change?
+ if( convertFastPropertyValue( pConvertedValues[ nHitCount ], pOldValues[nHitCount],
+ pHandles[i], pDelValues[i] ) )
+ {
+ // only increment if the property really change
+ pHandles[nHitCount] = pHandles[i];
+ nHitCount++;
+ }
+ }
+ }
+ // release guard to fire events
+ }
+
+ // fire vetoable events
+ fire( pHandles.get(), pConvertedValues.get(), pOldValues.get(), nHitCount, true );
+
+ // setting the agg Properties
+ m_xAggregateMultiSet->setPropertyValues(AggPropertyNames, AggValues);
+
+ {
+ // must lock the mutex outside the loop.
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ // Loop over all changed properties
+ for( i = 0; i < nHitCount; i++ )
+ {
+ // Will the property change?
+ setFastPropertyValue_NoBroadcast( pHandles[i], pConvertedValues[i] );
+ }
+ // release guard to fire events
+ }
+
+ // fire change events
+ fire( pHandles.get(), pConvertedValues.get(), pOldValues.get(), nHitCount, false );
+ }
+ else
+ m_xAggregateMultiSet->setPropertyValues(AggPropertyNames, AggValues);
+ }
+ }
+}
+
+// XPropertyState
+
+css::beans::PropertyState SAL_CALL OPropertySetAggregationHelper::getPropertyState(const OUString& _rPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName( _rPropertyName );
+
+ if (nHandle == -1)
+ {
+ throw css::beans::UnknownPropertyException(_rPropertyName);
+ }
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ return m_xAggregateState->getPropertyState(_rPropertyName);
+ else
+ return css::beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ return getPropertyStateByHandle(nHandle);
+}
+
+
+void SAL_CALL OPropertySetAggregationHelper::setPropertyToDefault(const OUString& _rPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName(_rPropertyName);
+ if (nHandle == -1)
+ {
+ throw css::beans::UnknownPropertyException(_rPropertyName);
+ }
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ m_xAggregateState->setPropertyToDefault(_rPropertyName);
+ }
+ else
+ {
+ try
+ {
+ setPropertyToDefaultByHandle( nHandle );
+ }
+ catch( const UnknownPropertyException& ) { throw; }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "OPropertySetAggregationHelper::setPropertyToDefault: caught an exception which is not allowed to leave here!" );
+ }
+ }
+}
+
+
+css::uno::Any SAL_CALL OPropertySetAggregationHelper::getPropertyDefault(const OUString& aPropertyName)
+{
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ sal_Int32 nHandle = rPH.getHandleByName( aPropertyName );
+
+ if ( nHandle == -1 )
+ throw css::beans::UnknownPropertyException(aPropertyName);
+
+ OUString aPropName;
+ sal_Int32 nOriginalHandle = -1;
+ if (rPH.fillAggregatePropertyInfoByHandle(&aPropName, &nOriginalHandle, nHandle))
+ {
+ if (m_xAggregateState.is())
+ return m_xAggregateState->getPropertyDefault(aPropertyName);
+ else
+ return css::uno::Any();
+ }
+ else
+ return getPropertyDefaultByHandle(nHandle);
+}
+
+sal_Bool SAL_CALL OPropertySetAggregationHelper::convertFastPropertyValue( Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ bool bModified = false;
+
+ OSL_ENSURE( m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::convertFastPropertyValue: this is no forwarded property - did you use declareForwardedProperty for it?" );
+ if ( m_pForwarder->isResponsibleFor( _nHandle ) )
+ {
+ // need to determine the type of the property for conversion
+ OPropertyArrayAggregationHelper& rPH = static_cast< OPropertyArrayAggregationHelper& >( getInfoHelper() );
+ Property aProperty;
+ OSL_VERIFY( rPH.getPropertyByHandle( _nHandle, aProperty ) );
+
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, _nHandle );
+ bModified = tryPropertyValue( _rConvertedValue, _rOldValue, _rValue, aCurrentValue, aProperty.Type );
+ }
+
+ return bModified;
+}
+
+void SAL_CALL OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue )
+{
+ OSL_ENSURE( m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::setFastPropertyValue_NoBroadcast: this is no forwarded property - did you use declareForwardedProperty for it?" );
+ if ( m_pForwarder->isResponsibleFor( _nHandle ) )
+ m_pForwarder->doForward( _nHandle, _rValue );
+}
+
+
+void OPropertySetAggregationHelper::declareForwardedProperty( sal_Int32 _nHandle )
+{
+ OSL_ENSURE( !m_pForwarder->isResponsibleFor( _nHandle ), "OPropertySetAggregationHelper::declareForwardedProperty: already declared!" );
+ m_pForwarder->takeResponsibilityFor( _nHandle );
+}
+
+
+void OPropertySetAggregationHelper::forwardingPropertyValue( sal_Int32 )
+{
+ // not interested in
+}
+
+
+void OPropertySetAggregationHelper::forwardedPropertyValue( sal_Int32 )
+{
+ // not interested in
+}
+
+
+bool OPropertySetAggregationHelper::isCurrentlyForwardingProperty( sal_Int32 _nHandle ) const
+{
+ return m_pForwarder->getCurrentlyForwardedProperty() == _nHandle;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/property.cxx b/comphelper/source/property/property.cxx
new file mode 100644
index 000000000..49a7a108f
--- /dev/null
+++ b/comphelper/source/property/property.cxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#if OSL_DEBUG_LEVEL > 0
+ #include <cppuhelper/exc_hlp.hxx>
+ #include <com/sun/star/lang/XServiceInfo.hpp>
+ #include <typeinfo>
+#endif
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <algorithm>
+
+
+namespace comphelper
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::cpp_queryInterface;
+ using ::com::sun::star::uno::cpp_acquire;
+ using ::com::sun::star::uno::cpp_release;
+#if OSL_DEBUG_LEVEL > 0
+ using ::com::sun::star::lang::XServiceInfo;
+#endif
+ using ::com::sun::star::uno::UNO_QUERY;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+
+void copyProperties(const Reference<XPropertySet>& _rxSource,
+ const Reference<XPropertySet>& _rxDest)
+{
+ if (!_rxSource.is() || !_rxDest.is())
+ {
+ OSL_FAIL("copyProperties: invalid arguments !");
+ return;
+ }
+
+ Reference< XPropertySetInfo > xSourceProps = _rxSource->getPropertySetInfo();
+ Reference< XPropertySetInfo > xDestProps = _rxDest->getPropertySetInfo();
+
+ const Sequence< Property > aSourceProps = xSourceProps->getProperties();
+ Property aDestProp;
+ for (const Property& rSourceProp : aSourceProps)
+ {
+ if ( xDestProps->hasPropertyByName(rSourceProp.Name) )
+ {
+ try
+ {
+ aDestProp = xDestProps->getPropertyByName(rSourceProp.Name);
+ if (0 == (aDestProp.Attributes & PropertyAttribute::READONLY) )
+ {
+ const Any aSourceValue = _rxSource->getPropertyValue(rSourceProp.Name);
+ if ( 0 != (aDestProp.Attributes & PropertyAttribute::MAYBEVOID) || aSourceValue.hasValue() )
+ _rxDest->setPropertyValue(rSourceProp.Name, aSourceValue);
+ }
+ }
+ catch (Exception&)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ OUStringBuffer aBuffer;
+ aBuffer.append( "::comphelper::copyProperties: could not copy property '" );
+ aBuffer.append(rSourceProp.Name );
+ aBuffer.append( "' to the destination set (a '" );
+
+ Reference< XServiceInfo > xSI( _rxDest, UNO_QUERY );
+ if ( xSI.is() )
+ {
+ aBuffer.append( xSI->getImplementationName() );
+ }
+ else
+ {
+ aBuffer.append( OUString::createFromAscii(typeid( *_rxDest ).name()) );
+ }
+ aBuffer.append( "' implementation).\n" );
+
+ Any aException( ::cppu::getCaughtException() );
+ aBuffer.append( "Caught an exception of type '" );
+ aBuffer.append( aException.getValueTypeName() );
+ aBuffer.append( "'" );
+
+ Exception aBaseException;
+ if ( ( aException >>= aBaseException ) && !aBaseException.Message.isEmpty() )
+ {
+ aBuffer.append( ", saying '" );
+ aBuffer.append( aBaseException.Message );
+ aBuffer.append( "'" );
+ }
+ aBuffer.append( "." );
+
+ SAL_WARN( "comphelper", aBuffer.makeStringAndClear() );
+#endif
+ }
+ }
+ }
+}
+
+
+bool hasProperty(const OUString& _rName, const Reference<XPropertySet>& _rxSet)
+{
+ if (_rxSet.is())
+ {
+ // XPropertySetInfoRef xInfo(rxSet->getPropertySetInfo());
+ return _rxSet->getPropertySetInfo()->hasPropertyByName(_rName);
+ }
+ return false;
+}
+
+
+void RemoveProperty(Sequence<Property>& _rProps, const OUString& _rPropName)
+{
+ // binary search
+ Property aNameProp(_rPropName, 0, Type(), 0);
+ const Property* pResult = std::lower_bound(std::cbegin(_rProps), std::cend(_rProps), aNameProp, PropertyCompareByName());
+
+ if ( pResult != std::cend(_rProps) && pResult->Name == _rPropName)
+ {
+ removeElementAt(_rProps, pResult - std::cbegin(_rProps));
+ }
+}
+
+
+void ModifyPropertyAttributes(Sequence<Property>& seqProps, const OUString& sPropName, sal_Int16 nAddAttrib, sal_Int16 nRemoveAttrib)
+{
+ // binary search
+ auto [begin, end] = asNonConstRange(seqProps);
+ Property aNameProp(sPropName, 0, Type(), 0);
+ Property* pResult = std::lower_bound(begin, end, aNameProp, PropertyCompareByName());
+
+ if ( (pResult != end) && (pResult->Name == sPropName) )
+ {
+ pResult->Attributes |= nAddAttrib;
+ pResult->Attributes &= ~nRemoveAttrib;
+ }
+}
+
+
+bool tryPropertyValue(Any& _rConvertedValue, Any& _rOldValue, const Any& _rValueToSet, const Any& _rCurrentValue, const Type& _rExpectedType)
+{
+ bool bModified(false);
+ if (_rCurrentValue.getValue() != _rValueToSet.getValue())
+ {
+ if ( _rValueToSet.hasValue() && ( !_rExpectedType.equals( _rValueToSet.getValueType() ) ) )
+ {
+ _rConvertedValue = Any( nullptr, _rExpectedType.getTypeLibType() );
+
+ if ( !uno_type_assignData(
+ const_cast< void* >( _rConvertedValue.getValue() ), _rConvertedValue.getValueType().getTypeLibType(),
+ const_cast< void* >( _rValueToSet.getValue() ), _rValueToSet.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(
+ cpp_queryInterface),
+ reinterpret_cast< uno_AcquireFunc >(cpp_acquire),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ )
+ )
+ throw css::lang::IllegalArgumentException();
+ }
+ else
+ _rConvertedValue = _rValueToSet;
+
+ if ( _rCurrentValue != _rConvertedValue )
+ {
+ _rOldValue = _rCurrentValue;
+ bModified = true;
+ }
+ }
+ return bModified;
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertybag.cxx b/comphelper/source/property/propertybag.cxx
new file mode 100644
index 000000000..02e6f78c1
--- /dev/null
+++ b/comphelper/source/property/propertybag.cxx
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propertybag.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyExistException.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NotRemoveableException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <map>
+#include <string_view>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::TypeClass_VOID;
+ using ::com::sun::star::beans::IllegalTypeException;
+ using ::com::sun::star::beans::PropertyExistException;
+ using ::com::sun::star::container::ElementExistException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::beans::NotRemoveableException;
+ using ::com::sun::star::beans::UnknownPropertyException;
+
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+
+ PropertyBag::PropertyBag()
+ : m_bAllowEmptyPropertyName(false)
+ {
+ }
+
+ PropertyBag::~PropertyBag()
+ {
+ }
+
+
+ void PropertyBag::setAllowEmptyPropertyName( bool i_isAllowed )
+ {
+ m_bAllowEmptyPropertyName = i_isAllowed;
+ }
+
+
+ namespace
+ {
+ void lcl_checkForEmptyName( const bool _allowEmpty, std::u16string_view _name )
+ {
+ if ( !_allowEmpty && _name.empty() )
+ throw IllegalArgumentException(
+ "The property name must not be empty.",
+ // TODO: resource
+ nullptr,
+ 1
+ );
+ }
+
+ void lcl_checkNameAndHandle_PropertyExistException( const OUString& _name, const sal_Int32 _handle, const PropertyBag& _container )
+ {
+ if ( _container.hasPropertyByName( _name ) || _container.hasPropertyByHandle( _handle ) )
+ throw PropertyExistException(
+ "Property name or handle already used.",
+ nullptr );
+
+ }
+
+ void lcl_checkNameAndHandle_ElementExistException( const OUString& _name, const sal_Int32 _handle, const PropertyBag& _container )
+ {
+ if ( _container.hasPropertyByName( _name ) || _container.hasPropertyByHandle( _handle ) )
+ throw ElementExistException(
+ "Property name or handle already used.",
+ nullptr );
+
+ }
+
+ }
+
+
+ void PropertyBag::addVoidProperty( const OUString& _rName, const Type& _rType, sal_Int32 _nHandle, sal_Int32 _nAttributes )
+ {
+ if ( _rType.getTypeClass() == TypeClass_VOID )
+ throw IllegalArgumentException(
+ "Illegal property type: VOID",
+ // TODO: resource
+ nullptr,
+ 1
+ );
+
+ // check name/handle sanity
+ lcl_checkForEmptyName( m_bAllowEmptyPropertyName, _rName );
+ lcl_checkNameAndHandle_ElementExistException( _rName, _nHandle, *this );
+
+ // register the property
+ OSL_ENSURE( _nAttributes & PropertyAttribute::MAYBEVOID, "PropertyBag::addVoidProperty: this is for default-void properties only!" );
+ registerPropertyNoMember( _rName, _nHandle, _nAttributes | PropertyAttribute::MAYBEVOID, _rType, css::uno::Any() );
+
+ // remember the default
+ aDefaults.emplace( _nHandle, Any() );
+ }
+
+
+ void PropertyBag::addProperty( const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes, const Any& _rInitialValue )
+ {
+ // check type sanity
+ const Type& aPropertyType = _rInitialValue.getValueType();
+ if ( aPropertyType.getTypeClass() == TypeClass_VOID )
+ throw IllegalTypeException(
+ "The initial value must be non-NULL to determine the property type.",
+ // TODO: resource
+ nullptr );
+
+ // check name/handle sanity
+ lcl_checkForEmptyName( m_bAllowEmptyPropertyName, _rName );
+ lcl_checkNameAndHandle_PropertyExistException( _rName, _nHandle, *this );
+
+ // register the property
+ registerPropertyNoMember( _rName, _nHandle, _nAttributes, aPropertyType,
+ _rInitialValue );
+
+ // remember the default
+ aDefaults.emplace( _nHandle, _rInitialValue );
+ }
+
+
+ void PropertyBag::removeProperty( const OUString& _rName )
+ {
+ const Property& rProp = getProperty( _rName );
+ // will throw an UnknownPropertyException if necessary
+ if ( ( rProp.Attributes & PropertyAttribute::REMOVABLE ) == 0 )
+ throw NotRemoveableException( OUString(), nullptr );
+ const sal_Int32 nHandle = rProp.Handle;
+
+ revokeProperty( nHandle );
+
+ aDefaults.erase( nHandle );
+ }
+
+
+ void PropertyBag::getFastPropertyValue( sal_Int32 _nHandle, Any& _out_rValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ OPropertyContainerHelper::getFastPropertyValue( _out_rValue, _nHandle );
+ }
+
+
+ bool PropertyBag::convertFastPropertyValue( sal_Int32 _nHandle, const Any& _rNewValue, Any& _out_rConvertedValue, Any& _out_rCurrentValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ return const_cast< PropertyBag* >( this )->OPropertyContainerHelper::convertFastPropertyValue(
+ _out_rConvertedValue, _out_rCurrentValue, _nHandle, _rNewValue );
+ }
+
+
+ void PropertyBag::setFastPropertyValue( sal_Int32 _nHandle, const Any& _rValue )
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+ }
+
+
+ void PropertyBag::getPropertyDefaultByHandle( sal_Int32 _nHandle, Any& _out_rValue ) const
+ {
+ if ( !hasPropertyByHandle( _nHandle ) )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+
+ auto pos = aDefaults.find( _nHandle );
+ OSL_ENSURE( pos != aDefaults.end(), "PropertyBag::getPropertyDefaultByHandle: inconsistency!" );
+ if ( pos != aDefaults.end() )
+ _out_rValue = pos->second;
+ else
+ _out_rValue.clear();
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertycontainer.cxx b/comphelper/source/property/propertycontainer.cxx
new file mode 100644
index 000000000..2b5685405
--- /dev/null
+++ b/comphelper/source/property/propertycontainer.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propertycontainer.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+OPropertyContainer::OPropertyContainer(::cppu::OBroadcastHelper& _rBHelper)
+ :OPropertySetHelper(_rBHelper)
+{
+}
+
+
+OPropertyContainer::~OPropertyContainer()
+{
+}
+
+
+Sequence< Type > OPropertyContainer::getBaseTypes()
+{
+ // just the types from our one and only base class
+ ::cppu::OTypeCollection aTypes(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get()
+ );
+ return aTypes.getTypes();
+}
+
+sal_Bool OPropertyContainer::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ return OPropertyContainerHelper::convertFastPropertyValue( _rConvertedValue, _rOldValue, _nHandle, _rValue );
+}
+
+
+void OPropertyContainer::setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle, const Any& _rValue)
+{
+ OPropertyContainerHelper::setFastPropertyValue( _nHandle, _rValue );
+}
+
+
+void OPropertyContainer::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ OPropertyContainerHelper::getFastPropertyValue( _rValue, _nHandle );
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertycontainerhelper.cxx b/comphelper/source/property/propertycontainerhelper.cxx
new file mode 100644
index 000000000..ee81100ae
--- /dev/null
+++ b/comphelper/source/property/propertycontainerhelper.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propertycontainerhelper.hxx>
+#include <comphelper/property.hxx>
+#include <osl/diagnose.h>
+#include <uno/data.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+
+#include <algorithm>
+#include <utility>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+
+namespace
+{
+ // comparing two property descriptions
+ struct PropertyDescriptionHandleCompare
+ {
+ bool operator() (const PropertyDescription& x, const PropertyDescription& y) const
+ {
+ return x.aProperty.Handle < y.aProperty.Handle;
+ }
+ };
+ // comparing two property descriptions (by name)
+ struct PropertyDescriptionNameMatch
+ {
+ OUString const m_rCompare;
+ explicit PropertyDescriptionNameMatch( OUString _aCompare ) : m_rCompare(std::move( _aCompare )) { }
+
+ bool operator() (const PropertyDescription& x ) const
+ {
+ return x.aProperty.Name == m_rCompare;
+ }
+ };
+}
+
+OPropertyContainerHelper::OPropertyContainerHelper()
+{
+}
+
+
+OPropertyContainerHelper::~OPropertyContainerHelper()
+{
+}
+
+
+void OPropertyContainerHelper::registerProperty(const OUString& _rName, sal_Int32 _nHandle,
+ sal_Int32 _nAttributes, void* _pPointerToMember, const Type& _rMemberType)
+{
+ OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) == 0,
+ "OPropertyContainerHelper::registerProperty: don't use this for properties which may be void ! There is a method called \"registerMayBeVoidProperty\" for this !");
+ OSL_ENSURE(!_rMemberType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerProperty: don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(_pPointerToMember,
+ "OPropertyContainerHelper::registerProperty: you gave me nonsense : the pointer must be non-NULL");
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rMemberType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassRealType;
+ aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
+
+ implPushBackProperty(aNewProp);
+}
+
+
+void OPropertyContainerHelper::revokeProperty( sal_Int32 _nHandle )
+{
+ PropertiesIterator aPos = searchHandle( _nHandle );
+ if ( aPos == m_aProperties.end() )
+ throw UnknownPropertyException(OUString::number(_nHandle));
+ m_aProperties.erase( aPos );
+}
+
+
+void OPropertyContainerHelper::registerMayBeVoidProperty(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
+ Any* _pPointerToMember, const Type& _rExpectedType)
+{
+ OSL_ENSURE((_nAttributes & PropertyAttribute::MAYBEVOID) != 0,
+ "OPropertyContainerHelper::registerMayBeVoidProperty: why calling this when the attributes say nothing about may-be-void ?");
+ OSL_ENSURE(!_rExpectedType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerMayBeVoidProperty: don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(_pPointerToMember,
+ "OPropertyContainerHelper::registerMayBeVoidProperty: you gave me nonsense : the pointer must be non-NULL");
+
+ _nAttributes |= PropertyAttribute::MAYBEVOID;
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rExpectedType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::DerivedClassAnyType;
+ aNewProp.aLocation.pDerivedClassMember = _pPointerToMember;
+
+ implPushBackProperty(aNewProp);
+}
+
+
+void OPropertyContainerHelper::registerPropertyNoMember(const OUString& _rName, sal_Int32 _nHandle, sal_Int32 _nAttributes,
+ const Type& _rType, css::uno::Any const & _pInitialValue)
+{
+ OSL_ENSURE(!_rType.equals(cppu::UnoType<Any>::get()),
+ "OPropertyContainerHelper::registerPropertyNoMember : don't give my the type of a uno::Any ! Really can't handle this !");
+ OSL_ENSURE(
+ (_pInitialValue.isExtractableTo(_rType)
+ || (!_pInitialValue.hasValue()
+ && (_nAttributes & PropertyAttribute::MAYBEVOID) != 0)),
+ "bad initial value");
+
+ PropertyDescription aNewProp;
+ aNewProp.aProperty = Property( _rName, _nHandle, _rType, static_cast<sal_Int16>(_nAttributes) );
+ aNewProp.eLocated = PropertyDescription::LocationType::HoldMyself;
+ aNewProp.aLocation.nOwnClassVectorIndex = m_aHoldProperties.size();
+ m_aHoldProperties.push_back(_pInitialValue);
+
+ implPushBackProperty(aNewProp);
+}
+
+
+bool OPropertyContainerHelper::isRegisteredProperty( sal_Int32 _nHandle ) const
+{
+ return const_cast< OPropertyContainerHelper* >( this )->searchHandle( _nHandle ) != m_aProperties.end();
+}
+
+
+bool OPropertyContainerHelper::isRegisteredProperty( const OUString& _rName ) const
+{
+ // TODO: the current structure is from a time where properties were
+ // static, not dynamic. Since we allow that properties are also dynamic,
+ // i.e. registered and revoked even though the XPropertySet has already been
+ // accessed, a vector is not really the best data structure anymore ...
+
+ return std::any_of(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ PropertyDescriptionNameMatch( _rName )
+ );
+}
+
+
+namespace
+{
+ struct ComparePropertyHandles
+ {
+ bool operator()( const PropertyDescription& _rLHS, const PropertyDescription& _nRHS ) const
+ {
+ return _rLHS.aProperty.Handle < _nRHS.aProperty.Handle;
+ }
+ };
+}
+
+
+void OPropertyContainerHelper::implPushBackProperty(const PropertyDescription& _rProp)
+{
+#ifdef DBG_UTIL
+ for (const auto& checkConflicts : m_aProperties)
+ {
+ OSL_ENSURE(checkConflicts.aProperty.Name != _rProp.aProperty.Name, "OPropertyContainerHelper::implPushBackProperty: name already exists!");
+ OSL_ENSURE(checkConflicts.aProperty.Handle != _rProp.aProperty.Handle, "OPropertyContainerHelper::implPushBackProperty: handle already exists!");
+ }
+#endif
+
+ PropertiesIterator pos = std::lower_bound(
+ m_aProperties.begin(), m_aProperties.end(),
+ _rProp, ComparePropertyHandles() );
+
+ m_aProperties.insert( pos, _rProp );
+}
+
+
+namespace
+{
+ void lcl_throwIllegalPropertyValueTypeException( const PropertyDescription& _rProperty, const Any& _rValue )
+ {
+ throw IllegalArgumentException(
+ "The given value cannot be converted to the required property type."
+ " (property name \"" + _rProperty.aProperty.Name
+ + "\", found value type \"" + _rValue.getValueType().getTypeName()
+ + "\", required property type \"" + _rProperty.aProperty.Type.getTypeName()
+ + "\")",
+ nullptr, 4 );
+ }
+}
+
+
+bool OPropertyContainerHelper::convertFastPropertyValue(
+ Any& _rConvertedValue, Any& _rOldValue, sal_Int32 _nHandle, const Any& _rValue )
+{
+ bool bModified = false;
+
+ // get the property somebody is asking for
+ PropertiesIterator aPos = searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::convertFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return bModified;
+ }
+
+ switch (aPos->eLocated)
+ {
+ // similar handling for the two cases where the value is stored in an any
+ case PropertyDescription::LocationType::HoldMyself:
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ {
+ bool bMayBeVoid = ((aPos->aProperty.Attributes & PropertyAttribute::MAYBEVOID) != 0);
+
+
+ // non modifiable version of the value-to-be-set
+ Any aNewRequestedValue( _rValue );
+
+ // normalization
+ // #i29490#
+ if ( !aNewRequestedValue.getValueType().equals( aPos->aProperty.Type ) )
+ { // the actually given value is not of the same type as the one required
+ Any aProperlyTyped( nullptr, aPos->aProperty.Type.getTypeLibType() );
+
+ if ( uno_type_assignData(
+ const_cast< void* >( aProperlyTyped.getValue() ), aProperlyTyped.getValueType().getTypeLibType(),
+ const_cast< void* >( aNewRequestedValue.getValue() ), aNewRequestedValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ )
+ )
+ {
+ // we were able to query the given XInterface-derivee for the interface
+ // which is required for this property
+ aNewRequestedValue = aProperlyTyped;
+ }
+ }
+
+ // argument check
+ if ( ! ( (bMayBeVoid && !aNewRequestedValue.hasValue()) // void is allowed if the attribute says so
+ || (aNewRequestedValue.getValueType().equals(aPos->aProperty.Type)) // else the types have to be equal
+ )
+ )
+ {
+ lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
+ }
+
+ Any* pPropContainer = nullptr;
+ // the pointer to the any which holds the property value, no matter if located in the derived class
+ // or in out vector
+
+ if (PropertyDescription::LocationType::HoldMyself == aPos->eLocated)
+ {
+ OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
+ "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
+ auto aIter = m_aHoldProperties.begin() + aPos->aLocation.nOwnClassVectorIndex;
+ pPropContainer = &(*aIter);
+ }
+ else
+ pPropContainer = static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
+
+ // check if the new value differs from the current one
+ if (!pPropContainer->hasValue() || !aNewRequestedValue.hasValue())
+ bModified = pPropContainer->hasValue() != aNewRequestedValue.hasValue();
+ else
+ bModified = !uno_type_equalData(
+ const_cast< void* >( pPropContainer->getValue() ), aPos->aProperty.Type.getTypeLibType(),
+ const_cast< void* >( aNewRequestedValue.getValue() ), aPos->aProperty.Type.getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ );
+
+ if (bModified)
+ {
+ _rOldValue = *pPropContainer;
+ _rConvertedValue = aNewRequestedValue;
+ }
+ }
+ break;
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ // let the UNO runtime library do any possible conversion
+ // this may include a change of the type - for instance, if a LONG is required,
+ // but a short is given, then this is valid, as it can be converted without any potential
+ // data loss
+
+ Any aProperlyTyped;
+ const Any* pNewValue = &_rValue;
+
+ if (!_rValue.getValueType().equals(aPos->aProperty.Type))
+ {
+ bool bConverted = false;
+
+ // a temporary any of the correct (required) type
+ aProperlyTyped = Any( nullptr, aPos->aProperty.Type.getTypeLibType() );
+ // (need this as we do not want to overwrite the derived class member here)
+
+ if ( uno_type_assignData(
+ const_cast<void*>(aProperlyTyped.getValue()), aProperlyTyped.getValueType().getTypeLibType(),
+ const_cast<void*>(_rValue.getValue()), _rValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ )
+ )
+ {
+ // could query for the requested interface
+ bConverted = true;
+ pNewValue = &aProperlyTyped;
+ }
+
+ if ( !bConverted )
+ lcl_throwIllegalPropertyValueTypeException( *aPos, _rValue );
+ }
+
+ // from here on, we should have the proper type
+ OSL_ENSURE( pNewValue->getValueType() == aPos->aProperty.Type,
+ "OPropertyContainerHelper::convertFastPropertyValue: conversion failed!" );
+ bModified = !uno_type_equalData(
+ aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
+ const_cast<void*>(pNewValue->getValue()), aPos->aProperty.Type.getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release )
+ );
+
+ if (bModified)
+ {
+ _rOldValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
+ _rConvertedValue = *pNewValue;
+ }
+ break;
+ }
+
+ return bModified;
+}
+
+
+void OPropertyContainerHelper::setFastPropertyValue(sal_Int32 _nHandle, const Any& _rValue)
+{
+ // get the property somebody is asking for
+ PropertiesIterator aPos = searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::setFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return;
+ }
+
+ bool bSuccess = true;
+
+ switch (aPos->eLocated)
+ {
+ case PropertyDescription::LocationType::HoldMyself:
+ m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex] = _rValue;
+ break;
+
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ *static_cast< Any* >(aPos->aLocation.pDerivedClassMember) = _rValue;
+ break;
+
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ // copy the data from the to-be-set value
+ bSuccess = uno_type_assignData(
+ aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type.getTypeLibType(),
+ const_cast< void* >( _rValue.getValue() ), _rValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >( cpp_queryInterface ),
+ reinterpret_cast< uno_AcquireFunc >( cpp_acquire ),
+ reinterpret_cast< uno_ReleaseFunc >( cpp_release ) );
+
+ OSL_ENSURE( bSuccess,
+ "OPropertyContainerHelper::setFastPropertyValue: ooops... the value could not be assigned!");
+
+ break;
+ }
+}
+
+void OPropertyContainerHelper::getFastPropertyValue(Any& _rValue, sal_Int32 _nHandle) const
+{
+ // get the property somebody is asking for
+ PropertiesIterator aPos = const_cast<OPropertyContainerHelper*>(this)->searchHandle(_nHandle);
+ if (aPos == m_aProperties.end())
+ {
+ OSL_FAIL( "OPropertyContainerHelper::getFastPropertyValue: unknown handle!" );
+ // should not happen if the derived class has built a correct property set info helper to be used by
+ // our base class OPropertySetHelper
+ return;
+ }
+
+ switch (aPos->eLocated)
+ {
+ case PropertyDescription::LocationType::HoldMyself:
+ OSL_ENSURE(aPos->aLocation.nOwnClassVectorIndex < m_aHoldProperties.size(),
+ "OPropertyContainerHelper::convertFastPropertyValue: invalid position !");
+ _rValue = m_aHoldProperties[aPos->aLocation.nOwnClassVectorIndex];
+ break;
+ case PropertyDescription::LocationType::DerivedClassAnyType:
+ _rValue = *static_cast<Any*>(aPos->aLocation.pDerivedClassMember);
+ break;
+ case PropertyDescription::LocationType::DerivedClassRealType:
+ _rValue.setValue(aPos->aLocation.pDerivedClassMember, aPos->aProperty.Type);
+ break;
+ }
+}
+
+
+OPropertyContainerHelper::PropertiesIterator OPropertyContainerHelper::searchHandle(sal_Int32 _nHandle)
+{
+ PropertyDescription aHandlePropDesc;
+ aHandlePropDesc.aProperty.Handle = _nHandle;
+ // search a lower bound
+ PropertiesIterator aLowerBound = std::lower_bound(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ aHandlePropDesc,
+ PropertyDescriptionHandleCompare());
+
+ // check for identity
+ if ((aLowerBound != m_aProperties.end()) && aLowerBound->aProperty.Handle != _nHandle)
+ aLowerBound = m_aProperties.end();
+
+ return aLowerBound;
+}
+
+
+const Property& OPropertyContainerHelper::getProperty( const OUString& _rName ) const
+{
+ ConstPropertiesIterator pos = std::find_if(
+ m_aProperties.begin(),
+ m_aProperties.end(),
+ PropertyDescriptionNameMatch( _rName )
+ );
+ if ( pos == m_aProperties.end() )
+ throw UnknownPropertyException( _rName );
+
+ return pos->aProperty;
+}
+
+
+void OPropertyContainerHelper::describeProperties(Sequence< Property >& _rProps) const
+{
+ Sequence< Property > aOwnProps(m_aProperties.size());
+ Property* pOwnProps = aOwnProps.getArray();
+
+ for (const auto& rProp : m_aProperties)
+ {
+ pOwnProps->Name = rProp.aProperty.Name;
+ pOwnProps->Handle = rProp.aProperty.Handle;
+ pOwnProps->Attributes = rProp.aProperty.Attributes;
+ pOwnProps->Type = rProp.aProperty.Type;
+ ++pOwnProps;
+ }
+
+ // as our property vector is sorted by handles, not by name, we have to sort aOwnProps
+ auto [begin, end] = asNonConstRange(aOwnProps);
+ std::sort(begin, end, PropertyCompareByName());
+
+ // unfortunately the STL merge function does not allow the output range to overlap one of the input ranges,
+ // so we need an extra sequence
+ Sequence< Property > aOutput(_rProps.getLength() + aOwnProps.getLength());
+ // do the merge
+ std::merge( std::cbegin(_rProps), std::cend(_rProps), // input 1
+ std::cbegin(aOwnProps), std::cend(aOwnProps), // input 2
+ aOutput.getArray(), // output
+ PropertyCompareByName() // compare operator
+ );
+
+ // copy the output
+ _rProps = aOutput;
+}
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertysethelper.cxx b/comphelper/source/property/propertysethelper.cxx
new file mode 100644
index 000000000..519b0705f
--- /dev/null
+++ b/comphelper/source/property/propertysethelper.cxx
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/propertysethelper.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+#include <memory>
+#include <utility>
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+static PropertyMapEntry const * find( const rtl::Reference<PropertySetInfo>& mxInfo, const OUString& aName ) noexcept
+{
+ PropertyMap::const_iterator aIter = mxInfo->getPropertyMap().find( aName );
+
+ if( mxInfo->getPropertyMap().end() != aIter )
+ return (*aIter).second;
+ else
+ return nullptr;
+}
+
+
+PropertySetHelper::PropertySetHelper( rtl::Reference<comphelper::PropertySetInfo> xInfo ) noexcept
+ : mxInfo(std::move(xInfo))
+{
+}
+
+PropertySetHelper::~PropertySetHelper() noexcept
+{
+}
+
+// XPropertySet
+Reference< XPropertySetInfo > SAL_CALL PropertySetHelper::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+void SAL_CALL PropertySetHelper::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ PropertyMapEntry const * aEntries[2];
+ aEntries[0] = find( mxInfo, aPropertyName );
+
+ if( nullptr == aEntries[0] )
+ throw UnknownPropertyException( aPropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ _setPropertyValues( aEntries, &aValue );
+}
+
+Any SAL_CALL PropertySetHelper::getPropertyValue( const OUString& PropertyName )
+{
+ PropertyMapEntry const * aEntries[2];
+ aEntries[0] = find( mxInfo, PropertyName );
+
+ if( nullptr == aEntries[0] )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ Any aAny;
+ _getPropertyValues( aEntries, &aAny );
+
+ return aAny;
+}
+
+void SAL_CALL PropertySetHelper::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+ // todo
+}
+
+// XMultiPropertySet
+void SAL_CALL PropertySetHelper::setPropertyValues( const Sequence< OUString >& rPropertyNames, const Sequence< Any >& rValues )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( nCount != rValues.getLength() )
+ throw IllegalArgumentException("lengths do not match", uno::Reference<uno::XInterface>(), -1);
+
+ if( !nCount )
+ return;
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+ pEntries[nCount] = nullptr;
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ bool bUnknown = false;
+ sal_Int32 n;
+ for( n = 0; !bUnknown && ( n < nCount ); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ if( !bUnknown )
+ _setPropertyValues( pEntries.get(), rValues.getConstArray() );
+
+ if( bUnknown )
+ throw RuntimeException( *pNames, static_cast< XPropertySet* >( this ) );
+}
+
+Sequence< Any > SAL_CALL PropertySetHelper::getPropertyValues(const Sequence< OUString >& rPropertyNames)
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+
+ if( !nCount )
+ return Sequence< Any >();
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ bool bUnknown = false;
+ sal_Int32 n;
+ for( n = 0; !bUnknown && ( n < nCount ); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ if( bUnknown )
+ throw RuntimeException( *pNames, static_cast< XPropertySet* >( this ) );
+
+ pEntries[nCount] = nullptr;
+ Sequence< Any > aValues(nCount);
+ aValues.realloc(nCount);
+ _getPropertyValues( pEntries.get(), aValues.getArray() );
+ return aValues;
+
+}
+
+void SAL_CALL PropertySetHelper::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+void SAL_CALL PropertySetHelper::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+ // todo
+}
+
+// XPropertyState
+PropertyState SAL_CALL PropertySetHelper::getPropertyState( const OUString& PropertyName )
+{
+ PropertyMapEntry const * aEntries[2];
+
+ aEntries[0] = find( mxInfo, PropertyName );
+ if( aEntries[0] == nullptr )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ aEntries[1] = nullptr;
+
+ PropertyState aState(PropertyState_AMBIGUOUS_VALUE);
+ _getPropertyStates( aEntries, &aState );
+
+ return aState;
+}
+
+Sequence< PropertyState > SAL_CALL PropertySetHelper::getPropertyStates( const Sequence< OUString >& aPropertyName )
+{
+ const sal_Int32 nCount = aPropertyName.getLength();
+
+ Sequence< PropertyState > aStates( nCount );
+
+ if( nCount )
+ {
+ const OUString* pNames = aPropertyName.getConstArray();
+
+ bool bUnknown = false;
+
+ std::unique_ptr<PropertyMapEntry const *[]> pEntries(new PropertyMapEntry const *[nCount+1]);
+
+ sal_Int32 n;
+ for( n = 0; !bUnknown && (n < nCount); n++, pNames++ )
+ {
+ pEntries[n] = find( mxInfo, *pNames );
+ bUnknown = nullptr == pEntries[n];
+ }
+
+ pEntries[nCount] = nullptr;
+
+ if( !bUnknown )
+ _getPropertyStates( pEntries.get(), aStates.getArray() );
+
+ if( bUnknown )
+ throw UnknownPropertyException( *pNames, static_cast< XPropertySet* >( this ) );
+ }
+
+ return aStates;
+}
+
+void SAL_CALL PropertySetHelper::setPropertyToDefault( const OUString& PropertyName )
+{
+ PropertyMapEntry const *pEntry = find(mxInfo, PropertyName );
+ if( nullptr == pEntry )
+ throw UnknownPropertyException( PropertyName, static_cast< XPropertySet* >( this ) );
+
+ _setPropertyToDefault( pEntry );
+}
+
+Any SAL_CALL PropertySetHelper::getPropertyDefault( const OUString& aPropertyName )
+{
+ PropertyMapEntry const * pEntry = find(mxInfo, aPropertyName );
+ if( nullptr == pEntry )
+ throw UnknownPropertyException( aPropertyName, static_cast< XPropertySet* >( this ) );
+
+ return _getPropertyDefault( pEntry );
+}
+
+void PropertySetHelper::_getPropertyStates(
+ const comphelper::PropertyMapEntry**, PropertyState*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+}
+
+void
+PropertySetHelper::_setPropertyToDefault(const comphelper::PropertyMapEntry*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+}
+
+Any PropertySetHelper::_getPropertyDefault(const comphelper::PropertyMapEntry*)
+{
+ OSL_FAIL( "you have to implement this yourself!");
+
+ Any aAny;
+ return aAny;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertysetinfo.cxx b/comphelper/source/property/propertysetinfo.cxx
new file mode 100644
index 000000000..206129c5f
--- /dev/null
+++ b/comphelper/source/property/propertysetinfo.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/sequence.hxx>
+
+
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+PropertySetInfo::PropertySetInfo() noexcept
+{
+}
+
+PropertySetInfo::PropertySetInfo( o3tl::span<const PropertyMapEntry> pMap ) noexcept
+{
+ maPropertyMap.reserve(pMap.size());
+ for (const auto & rEntry : pMap)
+ {
+ // check for duplicates
+ assert(maPropertyMap.find(rEntry.maName) == maPropertyMap.end());
+ // Make sure there are no accidental empty entries left at the end of the array from
+ // when this method used to take a empty-terminated array.
+ assert(!rEntry.maName.isEmpty());
+
+ maPropertyMap.emplace(rEntry.maName, &rEntry);
+ }
+}
+
+PropertySetInfo::~PropertySetInfo() noexcept
+{
+}
+
+void PropertySetInfo::add( o3tl::span<PropertyMapEntry const> pMap ) noexcept
+{
+ maPropertyMap.reserve(maPropertyMap.size() + pMap.size());
+ for (const auto & rEntry : pMap)
+ {
+ // check for duplicates
+ assert(maPropertyMap.find(rEntry.maName) == maPropertyMap.end());
+ // Make sure there are no accidental empty entries left at the end of the array from
+ // when this method used to take a empty-terminated array.
+ assert(!rEntry.maName.isEmpty());
+
+ maPropertyMap.emplace(rEntry.maName, &rEntry);
+ }
+
+ // clear cache
+ maProperties.realloc(0);
+}
+
+void PropertySetInfo::remove( const OUString& aName ) noexcept
+{
+ maPropertyMap.erase( aName );
+ maProperties.realloc(0);
+}
+
+Sequence< css::beans::Property > SAL_CALL PropertySetInfo::getProperties()
+{
+ // maybe we have to generate the properties after
+ // a change in the property map or at first call
+ // to getProperties
+ if( maProperties.size() != maPropertyMap.size() )
+ {
+ maProperties.realloc( maPropertyMap.size() );
+ auto propIter = maProperties.getArray();
+
+ for( const auto& rProperty : maPropertyMap )
+ {
+ PropertyMapEntry const * pEntry = rProperty.second;
+
+ propIter->Name = pEntry->maName;
+ propIter->Handle = pEntry->mnHandle;
+ propIter->Type = pEntry->maType;
+ propIter->Attributes = pEntry->mnAttributes;
+
+ ++propIter;
+ }
+ }
+ return maProperties;
+}
+
+Property SAL_CALL PropertySetInfo::getPropertyByName( const OUString& aName )
+{
+ PropertyMap::iterator aIter = maPropertyMap.find( aName );
+
+ if( maPropertyMap.end() == aIter )
+ throw UnknownPropertyException( aName );
+
+ PropertyMapEntry const * pEntry = (*aIter).second;
+
+ return Property( aName, pEntry->mnHandle, pEntry->maType, pEntry->mnAttributes );
+}
+
+sal_Bool SAL_CALL PropertySetInfo::hasPropertyByName( const OUString& aName )
+{
+ return maPropertyMap.find( aName ) != maPropertyMap.end();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propertystatecontainer.cxx b/comphelper/source/property/propertystatecontainer.cxx
new file mode 100644
index 000000000..e19e78733
--- /dev/null
+++ b/comphelper/source/property/propertystatecontainer.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <comphelper/propertystatecontainer.hxx>
+
+
+namespace comphelper
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::lang;
+
+ namespace
+ {
+ OUString lcl_getUnknownPropertyErrorMessage( std::u16string_view _rPropertyName )
+ {
+ // TODO: perhaps it's time to think about resources in the comphelper module?
+ // Would be nice to have localized exception strings (a simply resource file containing
+ // strings only would suffice, and could be realized with a UNO service, so we do not
+ // need the dependency to the Tools project)
+ return OUString::Concat("The property \"") + _rPropertyName + "\" is unknown.";
+ }
+ }
+
+ OPropertyStateContainer::OPropertyStateContainer( ::cppu::OBroadcastHelper& _rBHelper )
+ :OPropertyContainer( _rBHelper )
+ {
+ }
+
+
+ Any SAL_CALL OPropertyStateContainer::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OPropertyContainer::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OPropertyStateContainer_TBase::queryInterface( _rType );
+ return aReturn;
+ }
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( OPropertyStateContainer, OPropertyContainer, OPropertyStateContainer_TBase )
+
+
+ sal_Int32 OPropertyStateContainer::getHandleForName( const OUString& _rPropertyName )
+ {
+ // look up the handle for the name
+ ::cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName( _rPropertyName );
+
+ if ( -1 == nHandle )
+ throw UnknownPropertyException( lcl_getUnknownPropertyErrorMessage( _rPropertyName ), static_cast< XPropertyState* >( this ) );
+
+ return nHandle;
+ }
+
+
+ PropertyState SAL_CALL OPropertyStateContainer::getPropertyState( const OUString& _rPropertyName )
+ {
+ return getPropertyStateByHandle( getHandleForName( _rPropertyName ) );
+ }
+
+
+ Sequence< PropertyState > SAL_CALL OPropertyStateContainer::getPropertyStates( const Sequence< OUString >& _rPropertyNames )
+ {
+ sal_Int32 nProperties = _rPropertyNames.getLength();
+ Sequence< PropertyState> aStates( nProperties );
+ if ( !nProperties )
+ return aStates;
+
+#ifdef DBG_UTIL
+ // precondition: property sequence is sorted (the algorithm below relies on this)
+ {
+ const OUString* pNames = _rPropertyNames.getConstArray();
+ const OUString* pNamesCompare = pNames + 1;
+ const OUString* pNamesEnd = _rPropertyNames.getConstArray() + _rPropertyNames.getLength();
+ for ( ; pNamesCompare != pNamesEnd; ++pNames, ++pNamesCompare )
+ OSL_PRECOND( pNames->compareTo( *pNamesCompare ) < 0,
+ "OPropertyStateContainer::getPropertyStates: property sequence not sorted!" );
+ }
+#endif
+
+ const OUString* pLookup = _rPropertyNames.getConstArray();
+ const OUString* pLookupEnd = pLookup + nProperties;
+ PropertyState* pStates = aStates.getArray();
+
+ cppu::IPropertyArrayHelper& rHelper = getInfoHelper();
+ Sequence< Property> aAllProperties = rHelper.getProperties();
+ sal_Int32 nAllProperties = aAllProperties.getLength();
+ const Property* pAllProperties = aAllProperties.getConstArray();
+ const Property* pAllPropertiesEnd = pAllProperties + nAllProperties;
+
+ osl::MutexGuard aGuard( rBHelper.rMutex );
+ for ( ; ( pAllProperties != pAllPropertiesEnd ) && ( pLookup != pLookupEnd ); ++pAllProperties )
+ {
+#ifdef DBG_UTIL
+ if ( pAllProperties < pAllPropertiesEnd - 1 )
+ OSL_ENSURE( pAllProperties->Name.compareTo( (pAllProperties + 1)->Name ) < 0,
+ "OPropertyStateContainer::getPropertyStates: all-properties not sorted!" );
+#endif
+ if ( pAllProperties->Name == *pLookup )
+ {
+ *pStates++ = getPropertyState( *pLookup );
+ ++pLookup;
+ }
+ }
+
+ if ( pLookup != pLookupEnd )
+ // we run out of properties from the IPropertyArrayHelper, but still have properties to lookup
+ // -> we were asked for a nonexistent property
+ throw UnknownPropertyException( lcl_getUnknownPropertyErrorMessage( *pLookup ), static_cast< XPropertyState* >( this ) );
+
+ return aStates;
+ }
+
+
+ void SAL_CALL OPropertyStateContainer::setPropertyToDefault( const OUString& _rPropertyName )
+ {
+ setPropertyToDefaultByHandle( getHandleForName( _rPropertyName ) );
+ }
+
+
+ Any SAL_CALL OPropertyStateContainer::getPropertyDefault( const OUString& _rPropertyName )
+ {
+ Any aDefault;
+ getPropertyDefaultByHandle( getHandleForName( _rPropertyName ), aDefault );
+ return aDefault;
+ }
+
+
+ PropertyState OPropertyStateContainer::getPropertyStateByHandle( sal_Int32 _nHandle ) const
+ {
+ // simply compare the current and the default value
+ Any aCurrentValue;
+ getFastPropertyValue( aCurrentValue, _nHandle );
+ Any aDefaultValue;
+ getPropertyDefaultByHandle( _nHandle, aDefaultValue );
+
+ bool bEqual = uno_type_equalData(
+ const_cast< void* >( aCurrentValue.getValue() ), aCurrentValue.getValueType().getTypeLibType(),
+ const_cast< void* >( aDefaultValue.getValue() ), aDefaultValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ );
+ if ( bEqual )
+ return PropertyState_DEFAULT_VALUE;
+ else
+ return PropertyState_DIRECT_VALUE;
+ }
+
+
+ void OPropertyStateContainer::setPropertyToDefaultByHandle( sal_Int32 _nHandle )
+ {
+ Any aDefault;
+ getPropertyDefaultByHandle( _nHandle, aDefault );
+ setFastPropertyValue( _nHandle, aDefault );
+ }
+
+
+} // namespace comphelper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propmultiplex.cxx b/comphelper/source/property/propmultiplex.cxx
new file mode 100644
index 000000000..66a1545f8
--- /dev/null
+++ b/comphelper/source/property/propmultiplex.cxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <comphelper/propmultiplex.hxx>
+#include <osl/diagnose.h>
+
+
+namespace comphelper
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+OPropertyChangeListener::~OPropertyChangeListener()
+{
+ if (m_xAdapter.is())
+ m_xAdapter->dispose();
+}
+
+
+void OPropertyChangeListener::_disposing(const EventObject&)
+{
+ // nothing to do here
+}
+
+
+void OPropertyChangeListener::disposeAdapter()
+{
+ if ( m_xAdapter.is() )
+ m_xAdapter->dispose();
+
+ // will automatically set a new adapter
+ OSL_ENSURE( !m_xAdapter.is(), "OPropertyChangeListener::disposeAdapter: what did dispose do?" );
+}
+
+
+void OPropertyChangeListener::setAdapter(OPropertyChangeMultiplexer* pAdapter)
+{
+ ::osl::MutexGuard aGuard(m_rMutex);
+ m_xAdapter = pAdapter;
+}
+
+OPropertyChangeMultiplexer::OPropertyChangeMultiplexer(OPropertyChangeListener* _pListener, const Reference< XPropertySet>& _rxSet, bool _bAutoReleaseSet)
+ :m_xSet(_rxSet)
+ ,m_pListener(_pListener)
+ ,m_nLockCount(0)
+ ,m_bListening(false)
+ ,m_bAutoSetRelease(_bAutoReleaseSet)
+{
+ m_pListener->setAdapter(this);
+}
+
+
+OPropertyChangeMultiplexer::~OPropertyChangeMultiplexer()
+{
+}
+
+
+void OPropertyChangeMultiplexer::lock()
+{
+ ++m_nLockCount;
+}
+
+
+void OPropertyChangeMultiplexer::unlock()
+{
+ --m_nLockCount;
+}
+
+
+void OPropertyChangeMultiplexer::dispose()
+{
+ if (!m_bListening)
+ return;
+
+ Reference< XPropertyChangeListener> xPreventDelete(this);
+
+ for (const OUString& rProp : m_aProperties)
+ m_xSet->removePropertyChangeListener(rProp, static_cast< XPropertyChangeListener*>(this));
+
+ m_pListener->setAdapter(nullptr);
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ if (m_bAutoSetRelease)
+ m_xSet = nullptr;
+}
+
+// XEventListener
+
+void SAL_CALL OPropertyChangeMultiplexer::disposing( const EventObject& _rSource)
+{
+ if (m_pListener)
+ {
+ // tell the listener
+ if (!locked())
+ m_pListener->_disposing(_rSource);
+ // disconnect the listener
+ if (m_pListener) // may have been reset whilst calling into _disposing
+ m_pListener->setAdapter(nullptr);
+ }
+
+ m_pListener = nullptr;
+ m_bListening = false;
+
+ if (m_bAutoSetRelease)
+ m_xSet = nullptr;
+}
+
+// XPropertyChangeListener
+
+void SAL_CALL OPropertyChangeMultiplexer::propertyChange( const PropertyChangeEvent& _rEvent )
+{
+ if (m_pListener && !locked())
+ m_pListener->_propertyChanged(_rEvent);
+}
+
+
+void OPropertyChangeMultiplexer::addProperty(const OUString& _sPropertyName)
+{
+ if (m_xSet.is())
+ {
+ m_xSet->addPropertyChangeListener(_sPropertyName, static_cast< XPropertyChangeListener*>(this));
+ m_aProperties.push_back(_sPropertyName);
+ m_bListening = true;
+ }
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/comphelper/source/property/propstate.cxx b/comphelper/source/property/propstate.cxx
new file mode 100644
index 000000000..183f51efc
--- /dev/null
+++ b/comphelper/source/property/propstate.cxx
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/propstate.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/sequence.hxx>
+
+namespace comphelper
+{
+
+
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::lang::XTypeProvider;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::cpp_queryInterface;
+ using ::com::sun::star::uno::cpp_release;
+ using ::com::sun::star::beans::PropertyState_DEFAULT_VALUE;
+ using ::com::sun::star::beans::PropertyState_DIRECT_VALUE;
+
+
+ // OPropertyStateHelper
+
+
+ css::uno::Any SAL_CALL OPropertyStateHelper::queryInterface(const css::uno::Type& _rType)
+ {
+ css::uno::Any aReturn = OPropertySetHelper2::queryInterface(_rType);
+ // our own ifaces
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(_rType, static_cast< css::beans::XPropertyState*>(this));
+
+ return aReturn;
+ }
+
+
+ css::uno::Sequence<css::uno::Type> OPropertyStateHelper::getTypes()
+ {
+ return {
+ cppu::UnoType<css::beans::XPropertySet>::get(),
+ cppu::UnoType<css::beans::XMultiPropertySet>::get(),
+ cppu::UnoType<css::beans::XFastPropertySet>::get(),
+ cppu::UnoType<css::beans::XPropertySetOption>::get(),
+ cppu::UnoType<css::beans::XPropertyState>::get()};
+ }
+
+ OPropertyStateHelper::OPropertyStateHelper(
+ ::cppu::OBroadcastHelper& rBHlp,
+ ::cppu::IEventNotificationHook *i_pFireEvents)
+ : ::cppu::OPropertySetHelper2(rBHlp, i_pFireEvents) { }
+
+ OPropertyStateHelper::~OPropertyStateHelper() {}
+
+
+ void OPropertyStateHelper::firePropertyChange(sal_Int32 nHandle, const css::uno::Any& aNewValue, const css::uno::Any& aOldValue)
+ {
+ fire(&nHandle, &aNewValue, &aOldValue, 1, false);
+ }
+
+ // XPropertyState
+
+ css::beans::PropertyState SAL_CALL OPropertyStateHelper::getPropertyState(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ return getPropertyStateByHandle(nHandle);
+ }
+
+
+ void SAL_CALL OPropertyStateHelper::setPropertyToDefault(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ setPropertyToDefaultByHandle(nHandle);
+ }
+
+
+ css::uno::Any SAL_CALL OPropertyStateHelper::getPropertyDefault(const OUString& _rsName)
+ {
+ cppu::IPropertyArrayHelper& rPH = getInfoHelper();
+ sal_Int32 nHandle = rPH.getHandleByName(_rsName);
+
+ if (nHandle == -1)
+ throw css::beans::UnknownPropertyException(_rsName);
+
+ return getPropertyDefaultByHandle(nHandle);
+ }
+
+
+ css::uno::Sequence< css::beans::PropertyState> SAL_CALL OPropertyStateHelper::getPropertyStates(const css::uno::Sequence< OUString >& _rPropertyNames)
+ {
+ sal_Int32 nLen = _rPropertyNames.getLength();
+ css::uno::Sequence< css::beans::PropertyState> aRet(nLen);
+ css::beans::PropertyState* pValues = aRet.getArray();
+ const OUString* pNames = _rPropertyNames.getConstArray();
+
+ cppu::IPropertyArrayHelper& rHelper = getInfoHelper();
+
+ css::uno::Sequence< css::beans::Property> aProps = rHelper.getProperties();
+ const css::beans::Property* pProps = aProps.getConstArray();
+ sal_Int32 nPropCount = aProps.getLength();
+
+ osl::MutexGuard aGuard(rBHelper.rMutex);
+ for (sal_Int32 i=0, j=0; i<nPropCount && j<nLen; ++i, ++pProps)
+ {
+ // get the values only for valid properties
+ if (pProps->Name == *pNames)
+ {
+ *pValues = getPropertyState(*pNames);
+ ++pValues;
+ ++pNames;
+ ++j;
+ }
+ }
+
+ return aRet;
+ }
+
+
+ css::beans::PropertyState OPropertyStateHelper::getPropertyStateByHandle( sal_Int32 _nHandle )
+ {
+ // simply compare the current and the default value
+ Any aCurrentValue = getPropertyDefaultByHandle( _nHandle );
+ Any aDefaultValue;
+ getFastPropertyValue( aDefaultValue, _nHandle );
+
+ bool bEqual = uno_type_equalData(
+ const_cast< void* >( aCurrentValue.getValue() ), aCurrentValue.getValueType().getTypeLibType(),
+ const_cast< void* >( aDefaultValue.getValue() ), aDefaultValue.getValueType().getTypeLibType(),
+ reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
+ reinterpret_cast< uno_ReleaseFunc >(cpp_release)
+ );
+ return bEqual ? PropertyState_DEFAULT_VALUE : PropertyState_DIRECT_VALUE;
+ }
+
+
+ void OPropertyStateHelper::setPropertyToDefaultByHandle( sal_Int32 _nHandle )
+ {
+ setFastPropertyValue( _nHandle, getPropertyDefaultByHandle( _nHandle ) );
+ }
+
+
+ css::uno::Any OPropertyStateHelper::getPropertyDefaultByHandle( sal_Int32 ) const
+ {
+ return css::uno::Any();
+ }
+
+
+ // OStatefulPropertySet
+
+
+ OStatefulPropertySet::OStatefulPropertySet()
+ :OPropertyStateHelper( GetBroadcastHelper() )
+ {
+ }
+
+
+ OStatefulPropertySet::~OStatefulPropertySet()
+ {
+ }
+
+
+ Sequence< Type > SAL_CALL OStatefulPropertySet::getTypes()
+ {
+ return concatSequences(
+ Sequence {
+ cppu::UnoType<XWeak>::get(),
+ cppu::UnoType<XTypeProvider>::get() },
+ OPropertyStateHelper::getTypes()
+ );
+ }
+
+ Sequence< sal_Int8 > SAL_CALL OStatefulPropertySet::getImplementationId()
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ Any SAL_CALL OStatefulPropertySet::queryInterface( const Type& _rType )
+ {
+ Any aReturn = OWeakObject::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface( _rType, static_cast< XTypeProvider* >( this ) );
+ if ( !aReturn.hasValue() )
+ aReturn = OPropertyStateHelper::queryInterface( _rType );
+ return aReturn;
+ }
+
+
+ void SAL_CALL OStatefulPropertySet::acquire() noexcept
+ {
+ ::cppu::OWeakObject::acquire();
+ }
+
+
+ void SAL_CALL OStatefulPropertySet::release() noexcept
+ {
+ ::cppu::OWeakObject::release();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */