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