diff options
Diffstat (limited to 'comphelper/source/property')
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: */ |