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 throw (::com::sun::star::lang::IllegalArgumentException); 271 virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any_t& rValue ) 272 throw (::com::sun::star::uno::Exception); 273 virtual void SAL_CALL getFastPropertyValue( Any_t& rValue, sal_Int32 nHandle ) const; 274 275 virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper(); 276 virtual ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) throw(::com::sun::star::uno::RuntimeException); 277 278 public: 279 /// helper struct for granting selective access to some notification-related methods NotifierAccessPropertySetBase::NotifierAccess280 struct NotifierAccess { friend struct PropertyChangeNotifier; private: NotifierAccess() { } }; 281 /** retrieves the current property value for the given handle 282 @param nHandle 283 the handle of the property. Must denote a property supported by this instance, i.e. 284 one previously registered via <member>registerProperty</member>. 285 @see registerProperty 286 */ getCurrentPropertyValueByHandle(sal_Int32 nHandle,Any_t & rValue,const NotifierAccess &) const287 inline void getCurrentPropertyValueByHandle( sal_Int32 nHandle, Any_t& /* [out] */ rValue, const NotifierAccess& ) const 288 { 289 getFastPropertyValue( rValue, nHandle ); 290 } 291 292 /** notifies a change in a given property to all interested listeners 293 */ notifyPropertyChange(sal_Int32 nHandle,const Any_t & rOldValue,const Any_t & rNewValue,const NotifierAccess &) const294 inline void notifyPropertyChange( sal_Int32 nHandle, const Any_t& rOldValue, const Any_t& rNewValue, const NotifierAccess& ) const 295 { 296 const_cast< PropertySetBase* >( this )->firePropertyChange( nHandle, rNewValue, rOldValue ); 297 } 298 299 using ::comphelper::OStatefulPropertySet::getFastPropertyValue; 300 301 private: 302 /** locates a property given by handle 303 @param nHandle 304 the handle of the property. Must denote a property supported by this instance, i.e. 305 one previously registered via <member>registerProperty</member>. 306 @see registerProperty 307 */ 308 PropertyAccessorBase& locatePropertyHandler( sal_Int32 nHandle ) const; 309 }; 310 311 /** a helper class for notifying property changes in a <type>PropertySetBase</type> instance. 312 313 You can create an instance of this class on the stack of a method which is to programmatically 314 change the value of a property. In its constructor, the instance will acquire the current property 315 value, and in its destructor, it will notify the change of this property's value (if necessary). 316 317 You do not need this class if you are modifying property values by using the X(Fast|Multi)PropertSet 318 methods, since those already care for property notifications. You only need it if you're changing 319 the internal representation of your property directly. 320 321 Also note that usually, notifications in the UNO world should be done without a locked mutex. So 322 if you use this class in conjunction with a <type>MutexGuard</type>, ensure that you <b>first</b> 323 instantiate the <type>PropertyChangeNotifier</type>, and <b>then</b> the <type>MutexGuard</type>, 324 so your mutex is released before the notification happens. 325 */ 326 struct PropertyChangeNotifier 327 { 328 private: 329 const PropertySetBase& m_rPropertySet; 330 sal_Int32 m_nHandle; 331 com::sun::star::uno::Any m_aOldValue; 332 333 public: 334 /** constructs a PropertyChangeNotifier 335 @param rPropertySet 336 the property set implementation whose property is going to be changed. Note 337 that this property set implementation must live at least as long as the 338 PropertyChangeNotifier instance does. 339 @param nHandle 340 the handle of the property which is going to be changed. Must be a valid property 341 handle for the given <arg>rPropertySet</arg> 342 */ PropertyChangeNotifierPropertyChangeNotifier343 inline PropertyChangeNotifier( const PropertySetBase& rPropertySet, sal_Int32 nHandle ) 344 :m_rPropertySet( rPropertySet ) 345 ,m_nHandle( nHandle ) 346 { 347 m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, m_aOldValue, PropertySetBase::NotifierAccess() ); 348 } ~PropertyChangeNotifierPropertyChangeNotifier349 inline ~PropertyChangeNotifier() 350 { 351 com::sun::star::uno::Any aNewValue; 352 m_rPropertySet.getCurrentPropertyValueByHandle( m_nHandle, aNewValue, PropertySetBase::NotifierAccess() ); 353 if ( aNewValue != m_aOldValue ) 354 { 355 m_rPropertySet.notifyPropertyChange( m_nHandle, m_aOldValue, aNewValue, PropertySetBase::NotifierAccess() ); 356 } 357 } 358 }; 359 360 361 #define PROPERTY_FLAGS( NAME, TYPE, FLAG ) com::sun::star::beans::Property( \ 362 ::rtl::OUString( #NAME, sizeof( #NAME ) - 1, RTL_TEXTENCODING_ASCII_US ), \ 363 HANDLE_##NAME, getCppuType( static_cast< TYPE* >( NULL ) ), FLAG ) 364 #define PROPERTY( NAME, TYPE ) PROPERTY_FLAGS( NAME, TYPE, com::sun::star::beans::PropertyAttribute::BOUND ) 365 #define PROPERTY_RO( NAME, TYPE ) PROPERTY_FLAGS( NAME, TYPE, com::sun::star::beans::PropertyAttribute::BOUND | com::sun::star::beans::PropertyAttribute::READONLY ) 366 367 #endif 368