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