1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 #ifndef _PROPERTYSETBASE_HXX 28 #define _PROPERTYSETBASE_HXX 29 30 31 // include for parent class 32 #include <cppuhelper/weak.hxx> 33 #include <com/sun/star/lang/XTypeProvider.hpp> 34 #include <comphelper/propstate.hxx> 35 #include <comphelper/propertysetinfo.hxx> 36 #include <comphelper/proparrhlp.hxx> 37 #include <rtl/ref.hxx> 38 39 // include for inlined helper function below 40 #include <com/sun/star/lang/IllegalArgumentException.hpp> 41 #include <com/sun/star/beans/PropertyAttribute.hpp> 42 43 #include <map> 44 45 // forward declarations for method arguments 46 namespace com { namespace sun { namespace star { namespace uno { 47 class Any; 48 class Type; 49 class RuntimeException; 50 template<class T> class Sequence; 51 } } } } 52 53 /** base class which encapsulates accessing (reading/writing) concrete property values 54 */ 55 class PropertyAccessorBase : public ::rtl::IReference 56 { 57 private: 58 oslInterlockedCount m_refCount; 59 60 protected: 61 PropertyAccessorBase() : m_refCount( 0 ) { } 62 63 public: 64 virtual oslInterlockedCount SAL_CALL acquire(); 65 virtual oslInterlockedCount SAL_CALL release(); 66 67 virtual bool approveValue( const com::sun::star::uno::Any& rValue ) const = 0; 68 virtual void setValue( const com::sun::star::uno::Any& rValue ) = 0; 69 virtual void getValue( com::sun::star::uno::Any& rValue ) const = 0; 70 virtual bool isWriteable() const = 0; 71 }; 72 73 74 /** helper class for implementing property accessors through public member functions 75 */ 76 template< typename CLASS, typename VALUE, class WRITER, class READER > 77 class GenericPropertyAccessor : public PropertyAccessorBase 78 { 79 public: 80 typedef WRITER Writer; 81 typedef READER Reader; 82 83 private: 84 CLASS* m_pInstance; 85 Writer m_pWriter; 86 Reader m_pReader; 87 88 public: 89 GenericPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader ) 90 :m_pInstance( pInstance ) 91 ,m_pWriter( pWriter ) 92 ,m_pReader( pReader ) 93 { 94 } 95 96 virtual bool approveValue( const com::sun::star::uno::Any& rValue ) const 97 { 98 VALUE aVal; 99 return ( rValue >>= aVal ); 100 } 101 102 virtual void setValue( const com::sun::star::uno::Any& rValue ) 103 { 104 VALUE aTypedVal = VALUE(); 105 OSL_VERIFY( rValue >>= aTypedVal ); 106 (m_pInstance->*m_pWriter)( aTypedVal ); 107 } 108 109 virtual void getValue( com::sun::star::uno::Any& rValue ) const 110 { 111 rValue = com::sun::star::uno::makeAny( (m_pInstance->*m_pReader)() ); 112 } 113 114 virtual bool isWriteable() const 115 { 116 return m_pWriter != NULL; 117 } 118 }; 119 120 /** helper class for implementing property accessors via non-UNO methods 121 */ 122 template< typename CLASS, typename VALUE > 123 class DirectPropertyAccessor 124 :public GenericPropertyAccessor < CLASS 125 , VALUE 126 , void (CLASS::*)( const VALUE& ) 127 , VALUE (CLASS::*)() const 128 > 129 { 130 protected: 131 typedef void (CLASS::*Writer)( const VALUE& ); 132 typedef VALUE (CLASS::*Reader)() const; 133 public: 134 DirectPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader ) 135 :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader ) 136 { 137 } 138 }; 139 140 /** helper class for implementing non-UNO accessors to a boolean property 141 */ 142 template< typename CLASS, typename DUMMY > 143 class BooleanPropertyAccessor 144 :public GenericPropertyAccessor < CLASS 145 , bool 146 , void (CLASS::*)( bool ) 147 , bool (CLASS::*)() const 148 > 149 { 150 protected: 151 typedef void (CLASS::*Writer)( bool ); 152 typedef bool (CLASS::*Reader)() const; 153 public: 154 BooleanPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader ) 155 :GenericPropertyAccessor< CLASS, bool, Writer, Reader >( pInstance, pWriter, pReader ) 156 { 157 } 158 }; 159 160 /** helper class for implementing property accessors via UNO methods 161 */ 162 template< typename CLASS, typename VALUE > 163 class APIPropertyAccessor 164 :public GenericPropertyAccessor < CLASS 165 , VALUE 166 , void (SAL_CALL CLASS::*)( const VALUE& ) 167 , VALUE (SAL_CALL CLASS::*)() 168 > 169 { 170 protected: 171 typedef void (SAL_CALL CLASS::*Writer)( const VALUE& ); 172 typedef VALUE (SAL_CALL CLASS::*Reader)(); 173 public: 174 APIPropertyAccessor( CLASS* pInstance, Writer pWriter, Reader pReader ) 175 :GenericPropertyAccessor< CLASS, VALUE, Writer, Reader >( pInstance, pWriter, pReader ) 176 { 177 } 178 }; 179 180 /** bridges two XPropertySet helper implementations 181 182 The <type scope="comphelper">OStatefulPropertySet</type> (basically, the 183 <type scope="cppu">OPropertySetHelper</type>) implements a comprehensive framework 184 for property sets, including property change notifications. 185 However, it lacks some easy possibilities to declare the supported properties. 186 Other helper structs and classes allow for this, but are lacking needed features 187 such as property change notifications. 188 189 The <type>PropertySetBase</type> bridges various implementations, 190 so you have the best of both worlds. 191 */ 192 class PropertySetBase : public ::comphelper::OStatefulPropertySet 193 { 194 private: 195 typedef com::sun::star::uno::Any Any_t; 196 197 typedef ::std::map< const sal_Int32, ::rtl::Reference< PropertyAccessorBase > > PropertyAccessors; 198 typedef ::std::vector< ::com::sun::star::beans::Property > PropertyArray; 199 typedef ::std::map< const sal_Int32, Any_t > PropertyValueCache; 200 201 PropertyArray m_aProperties; 202 cppu::IPropertyArrayHelper* m_pProperties; 203 PropertyAccessors m_aAccessors; 204 PropertyValueCache m_aCache; 205 206 protected: 207 PropertySetBase(); 208 virtual ~PropertySetBase(); 209 210 /** registers a new property to be supported by this instance 211 @param rProperty 212 the descriptor for the to-be-supported property 213 @param rAccessor 214 an instance which is able to provide read and possibly write access to 215 the property. 216 @precond 217 Must not be called after any of the property set related UNO interfaces 218 has been used. Usually, you will do a number of <member>registerProperty</member> 219 calls in the constructor of your class. 220 */ 221 void registerProperty( 222 const com::sun::star::beans::Property& rProperty, 223 const ::rtl::Reference< PropertyAccessorBase >& rAccessor 224 ); 225 226 /** notifies a change in a given property value, if necessary 227 228 The necessity of the notification is determined by a cached value for the given 229 property. Caching happens after notification. 230 231 That is, when you call <member>notifyAndCachePropertyValue</member> for the first time, 232 a value for the given property is default constructed, and considered to be the "old value". 233 If this value differs from the current value, then this change is notified to all interested 234 listeners. Finally, the current value is remembered. 235 236 Subsequent calls to <member>notifyAndCachePropertyValue</member> use the remembered value as 237 "old value", and from then on behave as the first call. 238 239 @param nHandle 240 the handle of the property. Must denote a property supported by this instance, i.e. 241 one previously registered via <member>registerProperty</member>. 242 243 @precond 244 our ref count must not be 0. The reason is that during this method's execution, 245 the instance might be acquired and released, which would immediately destroy 246 the instance if it has a ref count of 0. 247 248 @seealso initializePropertyValueCache 249 */ 250 void notifyAndCachePropertyValue( sal_Int32 nHandle ); 251 252 /** initializes the property value cache for the given property, with its current value 253 254 Usually used to initialize the cache with values which are different from default 255 constructed values. Say you have a boolean property whose initial state 256 is <TRUE/>. Say you call <member>notifyAndCachePropertyValue</member> the first time: it will 257 default construct the "old value" for this property as <FALSE/>, and thus <b>not</b> do 258 any notifications if the "current value" is also <FALSE/> - which might be wrong, since 259 the guessing of the "old value" differed from the real initial value which was <TRUE/>. 260 261 Too confusing? Okay, than just call this method for every property you have. 262 263 @param nHandle 264 the handle of the property. Must denote a property supported by this instance, i.e. 265 one previously registered via <member>registerProperty</member>. 266 @param rValue 267 the value to cache 268 @seealso notifyAndCachePropertyValue 269 */ 270 void initializePropertyValueCache( sal_Int32 nHandle ); 271 272 /// OPropertysetHelper methods 273 virtual sal_Bool SAL_CALL convertFastPropertyValue( Any_t& rConvertedValue, Any_t& rOldValue, sal_Int32 nHandle, const Any_t& rValue ) 274 throw (::com::sun::star::lang::IllegalArgumentException); 275 virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any_t& rValue ) 276 throw (::com::sun::star::uno::Exception); 277 virtual void SAL_CALL getFastPropertyValue( Any_t& rValue, sal_Int32 nHandle ) const; 278 279 virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper(); 280 virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) throw(::com::sun::star::uno::RuntimeException); 281 282 public: 283 /// helper struct for granting selective access to some notification-related methods 284 struct NotifierAccess { friend struct PropertyChangeNotifier; private: NotifierAccess() { } }; 285 /** retrieves the current property value for the given handle 286 @param nHandle 287 the handle of the property. Must denote a property supported by this instance, i.e. 288 one previously registered via <member>registerProperty</member>. 289 @see registerProperty 290 */ 291 inline void getCurrentPropertyValueByHandle( sal_Int32 nHandle, Any_t& /* [out] */ rValue, const NotifierAccess& ) const 292 { 293 getFastPropertyValue( rValue, nHandle ); 294 } 295 296 /** notifies a change in a given property to all interested listeners 297 */ 298 inline void notifyPropertyChange( sal_Int32 nHandle, const Any_t& rOldValue, const Any_t& rNewValue, const NotifierAccess& ) const 299 { 300 const_cast< PropertySetBase* >( this )->firePropertyChange( nHandle, rNewValue, rOldValue ); 301 } 302 303 using ::comphelper::OStatefulPropertySet::getFastPropertyValue; 304 305 private: 306 /** locates a property given by handle 307 @param nHandle 308 the handle of the property. Must denote a property supported by this instance, i.e. 309 one previously registered via <member>registerProperty</member>. 310 @see registerProperty 311 */ 312 PropertyAccessorBase& locatePropertyHandler( sal_Int32 nHandle ) const; 313 }; 314 315 /** a helper class for notifying property changes in a <type>PropertySetBase</type> instance. 316 317 You can create an instance of this class on the stack of a method which is to programmatically 318 change the value of a property. In its constructor, the instance will acquire the current property 319 value, and in its destructor, it will notify the change of this property's value (if necessary). 320 321 You do not need this class if you are modifying property values by using the X(Fast|Multi)PropertSet 322 methods, since those already care for property notifications. You only need it if you're changing 323 the internal representation of your property directly. 324 325 Also note that usually, notifications in the UNO world should be done without a locked mutex. So 326 if you use this class in conjunction with a <type>MutexGuard</type>, ensure that you <b>first</b> 327 instantiate the <type>PropertyChangeNotifier</type>, and <b>then</b> the <type>MutexGuard</type>, 328 so your mutex is released before the notification happens. 329 */ 330 struct PropertyChangeNotifier 331 { 332 private: 333 const PropertySetBase& m_rPropertySet; 334 sal_Int32 m_nHandle; 335 com::sun::star::uno::Any m_aOldValue; 336 337 public: 338 /** constructs a PropertyChangeNotifier 339 @param rPropertySet 340 the property set implementation whose property is going to be changed. Note 341 that this property set implementation must live at least as long as the 342 PropertyChangeNotifier instance does. 343 @param nHandle 344 the handle of the property which is going to be changed. Must be a valid property 345 handle for the given <arg>rPropertySet</arg> 346 */ 347 inline PropertyChangeNotifier( const PropertySetBase& rPropertySet, sal_Int32 nHandle ) 348 :m_rPropertySet( rPropertySet ) 349 ,m_nHandle( nHandle ) 350 { 351 m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, m_aOldValue, PropertySetBase::NotifierAccess() ); 352 } 353 inline ~PropertyChangeNotifier() 354 { 355 com::sun::star::uno::Any aNewValue; 356 m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, aNewValue, PropertySetBase::NotifierAccess() ); 357 if ( aNewValue != m_aOldValue ) 358 { 359 m_rPropertySet.notifyPropertyChange( m_nHandle, m_aOldValue, aNewValue, PropertySetBase::NotifierAccess() ); 360 } 361 } 362 }; 363 364 365 #define PROPERTY_FLAGS( NAME, TYPE, FLAG ) com::sun::star::beans::Property( \ 366 ::rtl::OUString( #NAME, sizeof( #NAME ) - 1, RTL_TEXTENCODING_ASCII_US ), \ 367 HANDLE_##NAME, getCppuType( static_cast< TYPE* >( NULL ) ), FLAG ) 368 #define PROPERTY( NAME, TYPE ) PROPERTY_FLAGS( NAME, TYPE, com::sun::star::beans::PropertyAttribute::BOUND ) 369 #define PROPERTY_RO( NAME, TYPE ) PROPERTY_FLAGS( NAME, TYPE, com::sun::star::beans::PropertyAttribute::BOUND | com::sun::star::beans::PropertyAttribute::READONLY ) 370 371 #endif 372