diff options
Diffstat (limited to '')
-rw-r--r-- | cppuhelper/source/propshlp.cxx | 1184 |
1 files changed, 1184 insertions, 0 deletions
diff --git a/cppuhelper/source/propshlp.cxx b/cppuhelper/source/propshlp.cxx new file mode 100644 index 000000000..3af4cda49 --- /dev/null +++ b/cppuhelper/source/propshlp.cxx @@ -0,0 +1,1184 @@ +/* -*- 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 <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/propshlp.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <memory> +#include <sal/log.hxx> + +using namespace osl; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace cppu; + +namespace cppu { + +IPropertyArrayHelper::~IPropertyArrayHelper() +{ +} + +static const css::uno::Type & getPropertyTypeIdentifier( ) +{ + return cppu::UnoType<XPropertyChangeListener>::get(); +} +static const css::uno::Type & getPropertiesTypeIdentifier() +{ + return cppu::UnoType<XPropertiesChangeListener>::get(); +} +static const css::uno::Type & getVetoableTypeIdentifier() +{ + return cppu::UnoType<XVetoableChangeListener>::get(); +} + +extern "C" { + +static int compare_OUString_Property_Impl( const void *arg1, const void *arg2 ) + SAL_THROW_EXTERN_C() +{ + return static_cast<OUString const *>(arg1)->compareTo( static_cast<Property const *>(arg2)->Name ); +} + +} + +/** + * The class which implements the PropertySetInfo interface. + */ + +namespace { + +class OPropertySetHelperInfo_Impl + : public WeakImplHelper< css::beans::XPropertySetInfo > +{ + Sequence < Property > aInfos; + +public: + explicit OPropertySetHelperInfo_Impl( IPropertyArrayHelper & rHelper_ ); + + // XPropertySetInfo-methods + virtual Sequence< Property > SAL_CALL getProperties() override; + virtual Property SAL_CALL getPropertyByName(const OUString& PropertyName) override; + virtual sal_Bool SAL_CALL hasPropertyByName(const OUString& PropertyName) override; +}; + +} + +/** + * Create an object that implements XPropertySetInfo IPropertyArrayHelper. + */ +OPropertySetHelperInfo_Impl::OPropertySetHelperInfo_Impl( + IPropertyArrayHelper & rHelper_ ) + :aInfos( rHelper_.getProperties() ) +{ +} + +/** + * Return the sequence of properties, which are provided through the constructor. + */ +Sequence< Property > OPropertySetHelperInfo_Impl::getProperties() +{ + return aInfos; +} + +/** + * Return the sequence of properties, which are provided through the constructor. + */ +Property OPropertySetHelperInfo_Impl::getPropertyByName( const OUString & PropertyName ) +{ + Property * pR; + pR = static_cast<Property *>(bsearch( &PropertyName, aInfos.getConstArray(), aInfos.getLength(), + sizeof( Property ), + compare_OUString_Property_Impl )); + if( !pR ) { + throw UnknownPropertyException(PropertyName); + } + + return *pR; +} + +/** + * Return the sequence of properties, which are provided through the constructor. + */ +sal_Bool OPropertySetHelperInfo_Impl::hasPropertyByName( const OUString & PropertyName ) +{ + Property * pR; + pR = static_cast<Property *>(bsearch( &PropertyName, aInfos.getConstArray(), aInfos.getLength(), + sizeof( Property ), + compare_OUString_Property_Impl )); + return pR != nullptr; +} + + + +class OPropertySetHelper::Impl { + +public: + Impl( bool i_bIgnoreRuntimeExceptionsWhileFiring, + IEventNotificationHook *i_pFireEvents + ) + :m_bIgnoreRuntimeExceptionsWhileFiring( i_bIgnoreRuntimeExceptionsWhileFiring ) + ,m_bFireEvents(true) + ,m_pFireEvents( i_pFireEvents ) + { + } + + bool m_bIgnoreRuntimeExceptionsWhileFiring; + bool m_bFireEvents; + class IEventNotificationHook * const m_pFireEvents; + + std::vector< sal_Int32 > m_handles; + std::vector< Any > m_newValues; + std::vector< Any > m_oldValues; +}; + + + +OPropertySetHelper::OPropertySetHelper( + OBroadcastHelper & rBHelper_ ) + : rBHelper( rBHelper_ ), + aBoundLC( rBHelper_.rMutex ), + aVetoableLC( rBHelper_.rMutex ), + m_pReserved( new Impl(false, nullptr) ) +{ +} + +OPropertySetHelper::OPropertySetHelper( + OBroadcastHelper & rBHelper_, bool bIgnoreRuntimeExceptionsWhileFiring ) + : rBHelper( rBHelper_ ), + aBoundLC( rBHelper_.rMutex ), + aVetoableLC( rBHelper_.rMutex ), + m_pReserved( new Impl( bIgnoreRuntimeExceptionsWhileFiring, nullptr ) ) +{ +} + +OPropertySetHelper::OPropertySetHelper( + OBroadcastHelper & rBHelper_, IEventNotificationHook * i_pFireEvents, + bool bIgnoreRuntimeExceptionsWhileFiring) + : rBHelper( rBHelper_ ), + aBoundLC( rBHelper_.rMutex ), + aVetoableLC( rBHelper_.rMutex ), + m_pReserved( + new Impl( bIgnoreRuntimeExceptionsWhileFiring, i_pFireEvents) ) +{ +} + +OPropertySetHelper2::OPropertySetHelper2( + OBroadcastHelper & irBHelper, + IEventNotificationHook *i_pFireEvents, + bool bIgnoreRuntimeExceptionsWhileFiring) + :OPropertySetHelper( irBHelper, i_pFireEvents, bIgnoreRuntimeExceptionsWhileFiring ) +{ +} + +/** + * You must call disposing before. + */ +OPropertySetHelper::~OPropertySetHelper() +{ + delete m_pReserved; +} +OPropertySetHelper2::~OPropertySetHelper2() +{ +} + +// XInterface +Any OPropertySetHelper::queryInterface( const css::uno::Type & rType ) +{ + return ::cppu::queryInterface( + rType, + static_cast< XPropertySet * >( this ), + static_cast< XMultiPropertySet * >( this ), + static_cast< XFastPropertySet * >( this ) ); +} + +Any OPropertySetHelper2::queryInterface( const css::uno::Type & rType ) +{ + Any cnd(cppu::queryInterface(rType, static_cast< XPropertySetOption * >(this))); + if ( cnd.hasValue() ) + return cnd; + return OPropertySetHelper::queryInterface(rType); +} + +/** + * called from the derivee's XTypeProvider::getTypes implementation + */ +css::uno::Sequence< css::uno::Type > OPropertySetHelper::getTypes() +{ + return { + UnoType<css::beans::XPropertySet>::get(), + UnoType<css::beans::XMultiPropertySet>::get(), + UnoType<css::beans::XFastPropertySet>::get()}; +} + +// ComponentHelper +void OPropertySetHelper::disposing() +{ + // Create an event with this as sender + Reference < XPropertySet > rSource = this; + EventObject aEvt; + aEvt.Source = rSource; + + // inform all listeners to release this object + // The listener containers are automatically cleared + aBoundLC.disposeAndClear( aEvt ); + aVetoableLC.disposeAndClear( aEvt ); +} + +Reference < XPropertySetInfo > OPropertySetHelper::createPropertySetInfo( + IPropertyArrayHelper & rProperties ) +{ + return new OPropertySetHelperInfo_Impl(rProperties); +} + +// XPropertySet +void OPropertySetHelper::setPropertyValue( + const OUString& rPropertyName, const Any& rValue ) +{ + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + // call the method of the XFastPropertySet interface + setFastPropertyValue( nHandle, rValue ); +} + +// XPropertySet +Any OPropertySetHelper::getPropertyValue( + const OUString& rPropertyName ) +{ + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + // call the method of the XFastPropertySet interface + return getFastPropertyValue( nHandle ); +} + +// XPropertySet +void OPropertySetHelper::addPropertyChangeListener( + const OUString& rPropertyName, + const Reference < XPropertyChangeListener > & rxListener ) +{ + MutexGuard aGuard( rBHelper.rMutex ); + OSL_ENSURE( !rBHelper.bInDispose, "do not addPropertyChangeListener in the dispose call" ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + if( rBHelper.bInDispose || rBHelper.bDisposed ) + return; + + // only add listeners if you are not disposed + // a listener with no name means all properties + if( !rPropertyName.isEmpty() ) + { + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + if( nHandle == -1 ) { + // property not known throw exception + throw UnknownPropertyException(rPropertyName); + } + + sal_Int16 nAttributes; + rPH.fillPropertyMembersByHandle( nullptr, &nAttributes, nHandle ); + if( !(nAttributes & css::beans::PropertyAttribute::BOUND) ) + { + OSL_FAIL( "add listener to an unbound property" ); + // silent ignore this + return; + } + // add the change listener to the helper container + + aBoundLC.addInterface( nHandle, rxListener ); + } + else + // add the change listener to the helper container + rBHelper.aLC.addInterface( + getPropertyTypeIdentifier( ), + rxListener + ); +} + + +// XPropertySet +void OPropertySetHelper::removePropertyChangeListener( + const OUString& rPropertyName, + const Reference < XPropertyChangeListener >& rxListener ) +{ + MutexGuard aGuard( rBHelper.rMutex ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + // all listeners are automatically released in a dispose call + if( rBHelper.bInDispose || rBHelper.bDisposed ) + return; + + if( !rPropertyName.isEmpty() ) + { + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + if( nHandle == -1 ) + // property not known throw exception + throw UnknownPropertyException(rPropertyName); + aBoundLC.removeInterface( nHandle, rxListener ); + } + else { + // remove the change listener to the helper container + rBHelper.aLC.removeInterface( + getPropertyTypeIdentifier( ), + rxListener + ); + } +} + +// XPropertySet +void OPropertySetHelper::addVetoableChangeListener( + const OUString& rPropertyName, + const Reference< XVetoableChangeListener > & rxListener ) +{ + MutexGuard aGuard( rBHelper.rMutex ); + OSL_ENSURE( !rBHelper.bInDispose, "do not addVetoableChangeListener in the dispose call" ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + if( rBHelper.bInDispose || rBHelper.bDisposed ) + return; + + // only add listeners if you are not disposed + // a listener with no name means all properties + if( !rPropertyName.isEmpty() ) + { + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + if( nHandle == -1 ) { + // property not known throw exception + throw UnknownPropertyException(rPropertyName); + } + + sal_Int16 nAttributes; + rPH.fillPropertyMembersByHandle( nullptr, &nAttributes, nHandle ); + if( !(nAttributes & PropertyAttribute::CONSTRAINED) ) + { + OSL_FAIL( "addVetoableChangeListener, and property is not constrained" ); + // silent ignore this + return; + } + // add the vetoable listener to the helper container + aVetoableLC.addInterface( nHandle, rxListener ); + } + else + // add the vetoable listener to the helper container + rBHelper.aLC.addInterface( + getVetoableTypeIdentifier( ), + rxListener + ); +} + +// XPropertySet +void OPropertySetHelper::removeVetoableChangeListener( + const OUString& rPropertyName, + const Reference < XVetoableChangeListener > & rxListener ) +{ + MutexGuard aGuard( rBHelper.rMutex ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + // all listeners are automatically released in a dispose call + if( rBHelper.bInDispose || rBHelper.bDisposed ) + return; + + if( !rPropertyName.isEmpty() ) + { + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // map the name to the handle + sal_Int32 nHandle = rPH.getHandleByName( rPropertyName ); + if( nHandle == -1 ) { + // property not known throw exception + throw UnknownPropertyException(rPropertyName); + } + // remove the vetoable listener to the helper container + aVetoableLC.removeInterface( nHandle, rxListener ); + } + else + // add the vetoable listener to the helper container + rBHelper.aLC.removeInterface( + getVetoableTypeIdentifier( ), + rxListener + ); +} + +void OPropertySetHelper::setDependentFastPropertyValue( sal_Int32 i_handle, const css::uno::Any& i_value ) +{ + //OSL_PRECOND( rBHelper.rMutex.isAcquired(), "OPropertySetHelper::setDependentFastPropertyValue: to be called with a locked mutex only!" ); + // there is no such thing as Mutex.isAcquired, sadly ... + + sal_Int16 nAttributes(0); + IPropertyArrayHelper& rInfo = getInfoHelper(); + if ( !rInfo.fillPropertyMembersByHandle( nullptr, &nAttributes, i_handle ) ) + // unknown property + throw UnknownPropertyException(OUString::number(i_handle)); + + // no need to check for READONLY-ness of the property. The method is intended to be called internally, which + // implies it might be invoked for properties which are read-only to the instance's clients, but well allowed + // to change their value. + + Any aConverted, aOld; + bool bChanged = convertFastPropertyValue( aConverted, aOld, i_handle, i_value ); + if ( !bChanged ) + return; + + // don't fire vetoable events. This method is called with our mutex locked, so calling into listeners would not be + // a good idea. The caller is responsible for not invoking this for constrained properties. + OSL_ENSURE( ( nAttributes & PropertyAttribute::CONSTRAINED ) == 0, + "OPropertySetHelper::setDependentFastPropertyValue: not to be used for constrained properties!" ); + + // actually set the new value + try + { + setFastPropertyValue_NoBroadcast( i_handle, aConverted ); + } + catch (const UnknownPropertyException& ) { throw; /* allowed to leave */ } + catch (const PropertyVetoException& ) { throw; /* allowed to leave */ } + catch (const IllegalArgumentException& ) { throw; /* allowed to leave */ } + catch (const WrappedTargetException& ) { throw; /* allowed to leave */ } + catch (const RuntimeException& ) { throw; /* allowed to leave */ } + catch (const Exception& ) + { + // not allowed to leave this method + WrappedTargetException aWrapped; + aWrapped.TargetException = ::cppu::getCaughtException(); + aWrapped.Context = static_cast< XPropertySet* >( this ); + throw aWrapped; + } + + // remember the handle/values, for the events to be fired later + m_pReserved->m_handles.push_back( i_handle ); + m_pReserved->m_newValues.push_back( aConverted ); // TODO: setFastPropertyValue notifies the unconverted value here ...? + m_pReserved->m_oldValues.push_back( aOld ); +} + +// XFastPropertySet +void OPropertySetHelper::setFastPropertyValue( sal_Int32 nHandle, const Any& rValue ) +{ + OSL_ENSURE( !rBHelper.bInDispose, "do not setFastPropertyValue in the dispose call" ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + + IPropertyArrayHelper & rInfo = getInfoHelper(); + sal_Int16 nAttributes; + if( !rInfo.fillPropertyMembersByHandle( nullptr, &nAttributes, nHandle ) ) { + // unknown property + throw UnknownPropertyException(OUString::number(nHandle)); + } + if( nAttributes & PropertyAttribute::READONLY ) + throw PropertyVetoException(); + + Any aConvertedVal; + Any aOldVal; + + // Will the property change? + bool bChanged; + { + MutexGuard aGuard( rBHelper.rMutex ); + bChanged = convertFastPropertyValue( aConvertedVal, aOldVal, nHandle, rValue ); + // release guard to fire events + } + if( !bChanged ) + return; + + // Is it a constrained property? + if( nAttributes & PropertyAttribute::CONSTRAINED ) + { + // In aValue is the converted rValue + // fire a constrained event + // second parameter NULL means constrained + fire( &nHandle, &rValue, &aOldVal, 1, true ); + } + + { + MutexGuard aGuard( rBHelper.rMutex ); + try + { + // set the property to the new value + setFastPropertyValue_NoBroadcast( nHandle, aConvertedVal ); + } + catch (const css::beans::UnknownPropertyException& ) { throw; /* allowed to leave */ } + catch (const css::beans::PropertyVetoException& ) { throw; /* allowed to leave */ } + catch (const css::lang::IllegalArgumentException& ) { throw; /* allowed to leave */ } + catch (const css::lang::WrappedTargetException& ) { throw; /* allowed to leave */ } + catch (const css::uno::RuntimeException& ) { throw; /* allowed to leave */ } + catch (const css::uno::Exception& e ) + { + // not allowed to leave this method + css::lang::WrappedTargetException aWrap; + aWrap.Context = static_cast< css::beans::XPropertySet* >( this ); + aWrap.TargetException <<= e; + + throw aWrap; + } + + // release guard to fire events + } + // file a change event, if the value changed + impl_fireAll( &nHandle, &rValue, &aOldVal, 1 ); +} + +// XFastPropertySet +Any OPropertySetHelper::getFastPropertyValue( sal_Int32 nHandle ) + +{ + IPropertyArrayHelper & rInfo = getInfoHelper(); + if( !rInfo.fillPropertyMembersByHandle( nullptr, nullptr, nHandle ) ) + // unknown property + throw UnknownPropertyException(OUString::number(nHandle)); + + Any aRet; + MutexGuard aGuard( rBHelper.rMutex ); + getFastPropertyValue( aRet, nHandle ); + return aRet; +} + + +void OPropertySetHelper::impl_fireAll( sal_Int32* i_handles, const Any* i_newValues, const Any* i_oldValues, sal_Int32 i_count ) +{ + ClearableMutexGuard aGuard( rBHelper.rMutex ); + if ( m_pReserved->m_handles.empty() ) + { + aGuard.clear(); + fire( i_handles, i_newValues, i_oldValues, i_count, false ); + return; + } + + const size_t additionalEvents = m_pReserved->m_handles.size(); + OSL_ENSURE( additionalEvents == m_pReserved->m_newValues.size() + && additionalEvents == m_pReserved->m_oldValues.size(), + "OPropertySetHelper::impl_fireAll: inconsistency!" ); + + std::vector< sal_Int32 > allHandles( additionalEvents + i_count ); + std::copy( m_pReserved->m_handles.begin(), m_pReserved->m_handles.end(), allHandles.begin() ); + std::copy( i_handles, i_handles + i_count, allHandles.begin() + additionalEvents ); + + std::vector< Any > allNewValues( additionalEvents + i_count ); + std::copy( m_pReserved->m_newValues.begin(), m_pReserved->m_newValues.end(), allNewValues.begin() ); + std::copy( i_newValues, i_newValues + i_count, allNewValues.begin() + additionalEvents ); + + std::vector< Any > allOldValues( additionalEvents + i_count ); + std::copy( m_pReserved->m_oldValues.begin(), m_pReserved->m_oldValues.end(), allOldValues.begin() ); + std::copy( i_oldValues, i_oldValues + i_count, allOldValues.begin() + additionalEvents ); + + m_pReserved->m_handles.clear(); + m_pReserved->m_newValues.clear(); + m_pReserved->m_oldValues.clear(); + + aGuard.clear(); + fire( allHandles.data(), allNewValues.data(), allOldValues.data(), additionalEvents + i_count, false ); +} + + +void OPropertySetHelper::fire +( + sal_Int32 * pnHandles, + const Any * pNewValues, + const Any * pOldValues, + sal_Int32 nHandles, // This is the Count of the array + sal_Bool bVetoable +) +{ + if (! m_pReserved->m_bFireEvents) + return; + + if (m_pReserved->m_pFireEvents) { + m_pReserved->m_pFireEvents->fireEvents( + pnHandles, nHandles, bVetoable, + m_pReserved->m_bIgnoreRuntimeExceptionsWhileFiring); + } + + // Only fire, if one or more properties changed + if( !nHandles ) + return; + + // create the event sequence of all changed properties + Sequence< PropertyChangeEvent > aEvts( nHandles ); + PropertyChangeEvent * pEvts = aEvts.getArray(); + Reference < XInterface > xSource( static_cast<XPropertySet *>(this), UNO_QUERY ); + sal_Int32 i; + sal_Int32 nChangesLen = 0; + // Loop over all changed properties to fill the event struct + for( i = 0; i < nHandles; i++ ) + { + // Vetoable fire and constrained attribute set or + // Change fire and Changed and bound attribute set + IPropertyArrayHelper & rInfo = getInfoHelper(); + sal_Int16 nAttributes; + OUString aPropName; + rInfo.fillPropertyMembersByHandle( &aPropName, &nAttributes, pnHandles[i] ); + + if( + (bVetoable && (nAttributes & PropertyAttribute::CONSTRAINED)) || + (!bVetoable && (nAttributes & PropertyAttribute::BOUND)) + ) + { + pEvts[nChangesLen].Source = xSource; + pEvts[nChangesLen].PropertyName = aPropName; + pEvts[nChangesLen].PropertyHandle = pnHandles[i]; + pEvts[nChangesLen].OldValue = pOldValues[i]; + pEvts[nChangesLen].NewValue = pNewValues[i]; + nChangesLen++; + } + } + + bool bIgnoreRuntimeExceptionsWhileFiring = + m_pReserved->m_bIgnoreRuntimeExceptionsWhileFiring; + + // fire the events for all changed properties + for( i = 0; i < nChangesLen; i++ ) + { + // get the listener container for the property name + OInterfaceContainerHelper * pLC; + if( bVetoable ) // fire change Events? + pLC = aVetoableLC.getContainer( pEvts[i].PropertyHandle ); + else + pLC = aBoundLC.getContainer( pEvts[i].PropertyHandle ); + if( pLC ) + { + // Iterate over all listeners and send events + OInterfaceIteratorHelper aIt( *pLC); + while( aIt.hasMoreElements() ) + { + XInterface * pL = aIt.next(); + try + { + try + { + if( bVetoable ) // fire change Events? + { + static_cast<XVetoableChangeListener *>(pL)->vetoableChange( + pEvts[i] ); + } + else + { + static_cast<XPropertyChangeListener *>(pL)->propertyChange( + pEvts[i] ); + } + } + catch (DisposedException & exc) + { + OSL_ENSURE( exc.Context.is(), + "DisposedException without Context!" ); + if (exc.Context == pL) + aIt.remove(); + else + throw; + } + } + catch (RuntimeException & exc) + { + SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc); + if (! bIgnoreRuntimeExceptionsWhileFiring) + throw; + } + } + } + // broadcast to all listeners with "" property name + if( bVetoable ){ + // fire change Events? + pLC = rBHelper.aLC.getContainer( + getVetoableTypeIdentifier() + ); + } + else { + pLC = rBHelper.aLC.getContainer( + getPropertyTypeIdentifier( ) + ); + } + if( pLC ) + { + // Iterate over all listeners and send events. + OInterfaceIteratorHelper aIt( *pLC); + while( aIt.hasMoreElements() ) + { + XInterface * pL = aIt.next(); + try + { + try + { + if( bVetoable ) // fire change Events? + { + static_cast<XVetoableChangeListener *>(pL)->vetoableChange( + pEvts[i] ); + } + else + { + static_cast<XPropertyChangeListener *>(pL)->propertyChange( + pEvts[i] ); + } + } + catch (DisposedException & exc) + { + OSL_ENSURE( exc.Context.is(), + "DisposedException without Context!" ); + if (exc.Context == pL) + aIt.remove(); + else + throw; + } + } + catch (RuntimeException & exc) + { + SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc); + if (! bIgnoreRuntimeExceptionsWhileFiring) + throw; + } + } + } + } + + // reduce array to changed properties + aEvts.realloc( nChangesLen ); + + if( bVetoable ) + return; + + auto pCont = rBHelper.aLC.getContainer(getPropertiesTypeIdentifier()); + if (!pCont) + return; + + // Here is a Bug, unbound properties are also fired + OInterfaceIteratorHelper aIt( *pCont ); + while( aIt.hasMoreElements() ) + { + XPropertiesChangeListener * pL = + static_cast<XPropertiesChangeListener *>(aIt.next()); + try + { + try + { + // fire the whole event sequence to the + // XPropertiesChangeListener's + pL->propertiesChange( aEvts ); + } + catch (DisposedException & exc) + { + OSL_ENSURE( exc.Context.is(), + "DisposedException without Context!" ); + if (exc.Context == pL) + aIt.remove(); + else + throw; + } + } + catch (RuntimeException & exc) + { + SAL_INFO("cppuhelper", "caught RuntimeException while firing listeners: " << exc); + if (! bIgnoreRuntimeExceptionsWhileFiring) + throw; + } + } +} + +// OPropertySetHelper +void OPropertySetHelper::setFastPropertyValues( + sal_Int32 nSeqLen, + sal_Int32 * pHandles, + const Any * pValues, + sal_Int32 nHitCount ) +{ + OSL_ENSURE( !rBHelper.bInDispose, "do not getFastPropertyValue in the dispose call" ); + OSL_ENSURE( !rBHelper.bDisposed, "object is disposed" ); + + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + + std::unique_ptr<Any[]> pConvertedValues(new Any[ nHitCount ]); + std::unique_ptr<Any[]> pOldValues(new Any[ nHitCount ]); + sal_Int32 n = 0; + sal_Int32 i; + + { + // must lock the mutex outside the loop. So all values are consistent. + MutexGuard aGuard( rBHelper.rMutex ); + for( i = 0; i < nSeqLen; i++ ) + { + if( pHandles[i] != -1 ) + { + sal_Int16 nAttributes; + rPH.fillPropertyMembersByHandle( nullptr, &nAttributes, pHandles[i] ); + if( nAttributes & PropertyAttribute::READONLY ) { + throw PropertyVetoException(); + } + // Will the property change? + if( convertFastPropertyValue( pConvertedValues[ n ], pOldValues[n], + pHandles[i], pValues[i] ) ) + { + // only increment if the property really change + pHandles[n] = pHandles[i]; + n++; + } + } + } + // release guard to fire events + } + + // fire vetoable events + fire( pHandles, pConvertedValues.get(), pOldValues.get(), n, true ); + + { + // must lock the mutex outside the loop. + MutexGuard aGuard( rBHelper.rMutex ); + // Loop over all changed properties + for( i = 0; i < n; i++ ) + { + // Will the property change? + setFastPropertyValue_NoBroadcast( pHandles[i], pConvertedValues[i] ); + } + // release guard to fire events + } + + // fire change events + impl_fireAll( pHandles, pConvertedValues.get(), pOldValues.get(), n ); +} + +// XMultiPropertySet +/** + * The sequence may be contain not known properties. The implementation + * must ignore these properties. + */ +void OPropertySetHelper::setPropertyValues( + const Sequence<OUString>& rPropertyNames, + const Sequence<Any>& rValues ) +{ + sal_Int32 nSeqLen = rPropertyNames.getLength(); + std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ nSeqLen ]); + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // fill the handle array + sal_Int32 nHitCount = rPH.fillHandles( pHandles.get(), rPropertyNames ); + if( nHitCount != 0 ) + setFastPropertyValues( nSeqLen, pHandles.get(), rValues.getConstArray(), nHitCount ); +} + +// XMultiPropertySet +Sequence<Any> OPropertySetHelper::getPropertyValues( const Sequence<OUString>& rPropertyNames ) +{ + sal_Int32 nSeqLen = rPropertyNames.getLength(); + std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[ nSeqLen ]); + Sequence< Any > aValues( nSeqLen ); + + // get the map table + IPropertyArrayHelper & rPH = getInfoHelper(); + // fill the handle array + rPH.fillHandles( pHandles.get(), rPropertyNames ); + + Any * pValues = aValues.getArray(); + + MutexGuard aGuard( rBHelper.rMutex ); + // fill the sequence with the values + for( sal_Int32 i = 0; i < nSeqLen; i++ ) + getFastPropertyValue( pValues[i], pHandles[i] ); + + return aValues; +} + +// XMultiPropertySet +void OPropertySetHelper::addPropertiesChangeListener( + const Sequence<OUString> & , + const Reference < XPropertiesChangeListener > & rListener ) +{ + rBHelper.addListener( cppu::UnoType<decltype(rListener)>::get(), rListener ); +} + +// XMultiPropertySet +void OPropertySetHelper::removePropertiesChangeListener( + const Reference < XPropertiesChangeListener > & rListener ) +{ + rBHelper.removeListener( cppu::UnoType<decltype(rListener)>::get(), rListener ); +} + +// XMultiPropertySet +void OPropertySetHelper::firePropertiesChangeEvent( + const Sequence<OUString>& rPropertyNames, + const Reference < XPropertiesChangeListener >& rListener ) +{ + sal_Int32 nLen = rPropertyNames.getLength(); + std::unique_ptr<sal_Int32[]> pHandles(new sal_Int32[nLen]); + IPropertyArrayHelper & rPH = getInfoHelper(); + rPH.fillHandles( pHandles.get(), rPropertyNames ); + const OUString* pNames = rPropertyNames.getConstArray(); + + // get the count of matching properties + sal_Int32 nFireLen = 0; + sal_Int32 i; + for( i = 0; i < nLen; i++ ) + if( pHandles[i] != -1 ) + nFireLen++; + + Sequence<PropertyChangeEvent> aChanges( nFireLen ); + PropertyChangeEvent* pChanges = aChanges.getArray(); + + { + // must lock the mutex outside the loop. So all values are consistent. + MutexGuard aGuard( rBHelper.rMutex ); + Reference < XInterface > xSource( static_cast<XPropertySet *>(this), UNO_QUERY ); + sal_Int32 nFirePos = 0; + for( i = 0; i < nLen; i++ ) + { + if( pHandles[i] != -1 ) + { + pChanges[nFirePos].Source = xSource; + pChanges[nFirePos].PropertyName = pNames[i]; + pChanges[nFirePos].PropertyHandle = pHandles[i]; + getFastPropertyValue( pChanges[nFirePos].OldValue, pHandles[i] ); + pChanges[nFirePos].NewValue = pChanges[nFirePos].OldValue; + nFirePos++; + } + } + // release guard to fire events + } + if( nFireLen ) + rListener->propertiesChange( aChanges ); +} + +void OPropertySetHelper2::enableChangeListenerNotification( sal_Bool bEnable ) +{ + m_pReserved->m_bFireEvents = bEnable; +} + +extern "C" { + +static int compare_Property_Impl( const void *arg1, const void *arg2 ) + SAL_THROW_EXTERN_C() +{ + return static_cast<Property const *>(arg1)->Name.compareTo( static_cast<Property const *>(arg2)->Name ); +} + +} + +void OPropertyArrayHelper::init( sal_Bool bSorted ) +{ + sal_Int32 i, nElements = aInfos.getLength(); + const Property* pProperties = aInfos.getConstArray(); + + for( i = 1; i < nElements; i++ ) + { + if( pProperties[i-1].Name > pProperties[i].Name ) + { + if (bSorted) { + OSL_FAIL( "Property array is not sorted" ); + } + // not sorted + qsort( aInfos.getArray(), nElements, sizeof( Property ), + compare_Property_Impl ); + pProperties = aInfos.getConstArray(); + break; + } + } + for( i = 0; i < nElements; i++ ) + if( pProperties[i].Handle != i ) + return; + // The handle is the index + bRightOrdered = true; +} + +OPropertyArrayHelper::OPropertyArrayHelper( + Property * pProps, + sal_Int32 nEle, + sal_Bool bSorted ) + : m_pReserved(nullptr) + , aInfos(pProps, nEle) + , bRightOrdered( false ) +{ + init( bSorted ); +} + +OPropertyArrayHelper::OPropertyArrayHelper( + const Sequence< Property > & aProps, + sal_Bool bSorted ) + : m_pReserved(nullptr) + , aInfos(aProps) + , bRightOrdered( false ) +{ + init( bSorted ); +} + + +sal_Int32 OPropertyArrayHelper::getCount() const +{ + return aInfos.getLength(); +} + + +sal_Bool OPropertyArrayHelper::fillPropertyMembersByHandle +( + OUString * pPropName, + sal_Int16 * pAttributes, + sal_Int32 nHandle +) +{ + const Property* pProperties = aInfos.getConstArray(); + sal_Int32 nElements = aInfos.getLength(); + + if( bRightOrdered ) + { + if( nHandle < 0 || nHandle >= nElements ) + return false; + if( pPropName ) + *pPropName = pProperties[ nHandle ].Name; + if( pAttributes ) + *pAttributes = pProperties[ nHandle ].Attributes; + return true; + } + // normally the array is sorted + for( sal_Int32 i = 0; i < nElements; i++ ) + { + if( pProperties[i].Handle == nHandle ) + { + if( pPropName ) + *pPropName = pProperties[ i ].Name; + if( pAttributes ) + *pAttributes = pProperties[ i ].Attributes; + return true; + } + } + return false; +} + + +Sequence< Property > OPropertyArrayHelper::getProperties() +{ + return aInfos; +} + + +Property OPropertyArrayHelper::getPropertyByName(const OUString& aPropertyName) +{ + Property * pR; + pR = static_cast<Property *>(bsearch( &aPropertyName, aInfos.getConstArray(), aInfos.getLength(), + sizeof( Property ), + compare_OUString_Property_Impl )); + if( !pR ) { + throw UnknownPropertyException(aPropertyName); + } + return *pR; +} + + +sal_Bool OPropertyArrayHelper::hasPropertyByName(const OUString& aPropertyName) +{ + Property * pR; + pR = static_cast<Property *>(bsearch( &aPropertyName, aInfos.getConstArray(), aInfos.getLength(), + sizeof( Property ), + compare_OUString_Property_Impl )); + return pR != nullptr; +} + + +sal_Int32 OPropertyArrayHelper::getHandleByName( const OUString & rPropName ) +{ + Property * pR; + pR = static_cast<Property *>(bsearch( &rPropName, aInfos.getConstArray(), aInfos.getLength(), + sizeof( Property ), + compare_OUString_Property_Impl )); + return pR ? pR->Handle : -1; +} + + +sal_Int32 OPropertyArrayHelper::fillHandles( sal_Int32 * pHandles, const Sequence< OUString > & rPropNames ) +{ + sal_Int32 nHitCount = 0; + const OUString * pReqProps = rPropNames.getConstArray(); + sal_Int32 nReqLen = rPropNames.getLength(); + const Property * pCur = aInfos.getConstArray(); + const Property * pEnd = pCur + aInfos.getLength(); + + for( sal_Int32 i = 0; i < nReqLen; i++ ) + { + // Calculate logarithm + sal_Int32 n = static_cast<sal_Int32>(pEnd - pCur); + sal_Int32 nLog = 0; + while( n ) + { + nLog += 1; + n = n >> 1; + } + + // Number of properties to search for * Log2 of the number of remaining + // properties to search in. + if( (nReqLen - i) * nLog >= pEnd - pCur ) + { + // linear search is better + while( pCur < pEnd && pReqProps[i] > pCur->Name ) + { + pCur++; + } + if( pCur < pEnd && pReqProps[i] == pCur->Name ) + { + pHandles[i] = pCur->Handle; + nHitCount++; + } + else + pHandles[i] = -1; + } + else + { + // binary search is better + sal_Int32 nCompVal = 1; + const Property * pOldEnd = pEnd--; + const Property * pMid = pCur; + + while( nCompVal != 0 && pCur <= pEnd ) + { + pMid = (pEnd - pCur) / 2 + pCur; + + nCompVal = pReqProps[i].compareTo( pMid->Name ); + + if( nCompVal > 0 ) + pCur = pMid + 1; + else + pEnd = pMid - 1; + } + + if( nCompVal == 0 ) + { + pHandles[i] = pMid->Handle; + nHitCount++; + pCur = pMid +1; + } + else if( nCompVal > 0 ) + { + pHandles[i] = -1; + pCur = pMid +1; + } + else + { + pHandles[i] = -1; + pCur = pMid; + } + pEnd = pOldEnd; + } + } + return nHitCount; +} + +} // end namespace cppu + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |