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