xref: /AOO41X/main/comphelper/inc/comphelper/accessiblecontexthelper.hxx (revision 9877b273795ec465a3ce9c15d738eb34c0455705)
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 
24 #ifndef COMPHELPER_ACCESSIBLE_CONTEXT_HELPER_HXX
25 #define COMPHELPER_ACCESSIBLE_CONTEXT_HELPER_HXX
26 
27 #include <cppuhelper/compbase2.hxx>
28 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
29 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
30 #include <com/sun/star/lang/DisposedException.hpp>
31 #include <comphelper/broadcasthelper.hxx>
32 #include "comphelper/comphelperdllapi.h"
33 
34 //.........................................................................
35 namespace comphelper
36 {
37 //.........................................................................
38 
39     class AccessibleEventBuffer;
40 
41     //=====================================================================
42     //= IMutex
43     //=====================================================================
44 
45     // This whole thingie here (own mutex classes and such) is a HACK. I hate the SolarMutex.
46     // See below for more explanations ....
47 
48     /** abstract interface for implementing a mutex
49     */
50     class IMutex
51     {
52     public:
53         virtual void acquire() = 0;
54         virtual void release() = 0;
55     };
56 
57     //=====================================================================
58     //= OMutexGuard
59     //=====================================================================
60 
61     class OMutexGuard
62     {
63         IMutex* m_pMutex;
64     public:
OMutexGuard(IMutex * _pMutex)65         inline OMutexGuard( IMutex* _pMutex )
66             :m_pMutex( _pMutex )
67         {
68             if ( m_pMutex )
69                 m_pMutex->acquire();
70         }
71 
~OMutexGuard()72         inline ~OMutexGuard( )
73         {
74             if ( m_pMutex )
75                 m_pMutex->release();
76         }
77     };
78 
79     //=====================================================================
80     //= OAccessibleContextHelper
81     //=====================================================================
82 
83     class OContextHelper_Impl;
84     typedef ::cppu::WeakAggComponentImplHelper2 <   ::com::sun::star::accessibility::XAccessibleContext,
85                                                     ::com::sun::star::accessibility::XAccessibleEventBroadcaster
86                                                 >   OAccessibleContextHelper_Base;
87 
88     /** helper class for implementing an AccessibleContext
89     */
90     class COMPHELPER_DLLPUBLIC OAccessibleContextHelper
91                 :public ::comphelper::OBaseMutex
92                 ,public OAccessibleContextHelper_Base
93     {
94     private:
95         OContextHelper_Impl*    m_pImpl;
96 
97     protected:
98         OAccessibleContextHelper( );
99         ~OAccessibleContextHelper( );
100 
101         /** ctor
102 
103             <p>If you need additional object safety for your class, and want to ensure that your own
104             mutex is locked before the mutex this class provides is, than use this ctor.</p>
105 
106             <p>Beware that this is a hack. Unfortunately, OpenOffice.org has two different mutex hierarchies,
107             which are not compatible. In addition, wide parts of the code (especially VCL) is not thread-safe,
108             but instead relies on a <em>single global mutex</em>. As a consequence, components using
109             directly or indirectly such code need to care for this global mutex. Yes, this is as ugly as
110             anything.</p>
111 
112             <p>Note that the external lock is used as additional lock, not as the only one. The own mutex of the
113             instance is used for internal actions, and every action which potentially involves external code
114             (for instance every call to a virtual method overridden by derivees) is <em>additionally</em> and
115             <em>first</em> guarded by with the external lock.</p>
116 
117             <p>Beware of the lifetime of the lock - you must ensure that the lock exists at least as long as
118             the context does. A good approach to implement the lock may be to derive you own context
119             not only from OAccessibleContextHelper, but also from IMutex.</p>
120 
121             <p>One more note. This lock is definately not used once the dtor is reached. Means whatever
122             the dtor implementation does, it does <em>not</em> guard the external lock. See this as a contract.
123             <br/>You should ensure the same thing for own derivees which do not supply the lock themself,
124             but get them from yet another derivee.</p>
125             @see forgetExternalLock
126         */
127         OAccessibleContextHelper( IMutex* _pExternalLock );
128 
129         /** late construction
130         @param _rxAccessible
131             the Accessible object which created this context.
132             <p>If your derived implementation implements the XAccessible (and does not follow the proposed
133             separation of XAccessible from XAccessibleContext), you may pass <code>this</code> here.</p>
134 
135             <p>The object is hold weak, so it's life time is not affected.</p>
136 
137             <p>The object is needed for performance reasons: for <method>getAccessibleIndexInParent</method>,
138             all children (which are XAccessible's theirself) of our parent have to be asked. If we know our
139             XAccessible, we can compare it with all the children, instead of asking all children for their
140             context and comparing this context with ourself.</p>
141         */
142         void    lateInit( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& _rxAccessible );
143 
144         /** retrieves the creator previously set with <method>lateInit</method>
145         */
146         ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >
147                 getAccessibleCreator( ) const;
148 
149         /** forgets the reference to the external lock, if present.
150 
151             <p>This means any further locking will not be guard the external lock anymore, never.</p>
152 
153             <p>To be used in derived classes which do not supply the external lock themself, but instead get
154             them passed from own derivees (or clients).</p>
155         */
156         void    forgetExternalLock();
157 
158     public:
159         // XAccessibleEventBroadcaster
160         using WeakAggComponentImplHelperBase::addEventListener;
161         virtual void SAL_CALL addEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException);
162         using WeakAggComponentImplHelperBase::removeEventListener;
163         virtual void SAL_CALL removeEventListener( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException);
164 
165         // XAccessibleContext - still waiting to be overwritten
166         virtual sal_Int32 SAL_CALL getAccessibleChildCount(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
167         virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException) = 0;
168         virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > SAL_CALL getAccessibleParent(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
169         virtual sal_Int16 SAL_CALL getAccessibleRole(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
170         virtual ::rtl::OUString SAL_CALL getAccessibleDescription(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
171         virtual ::rtl::OUString SAL_CALL getAccessibleName(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
172         virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
173         virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet(  ) throw (::com::sun::star::uno::RuntimeException) = 0;
174 
175         // XAccessibleContext - default implementations
176         /** default implementation for retrieving the index of this object within the parent
177             <p>This basic implementation here returns the index <code>i</code> of the child for which
178                 <code>&lt;parent&gt;.getAccessibleChild( i )</code> equals our creator.</p>
179         */
180         virtual sal_Int32 SAL_CALL getAccessibleIndexInParent(  ) throw (::com::sun::star::uno::RuntimeException);
181         /** default implementation for retrieving the locale
182             <p>This basic implementation returns the locale of the parent context,
183             as retrieved via getAccessibleParent()->getAccessibleContext.</p>
184         */
185         virtual ::com::sun::star::lang::Locale SAL_CALL getLocale(  ) throw (::com::sun::star::accessibility::IllegalAccessibleComponentStateException, ::com::sun::star::uno::RuntimeException);
186 
187     public:
188         // helper struct for granting selective access rights
189         struct OAccessControl
190         {
191             friend class OContextEntryGuard;
192             friend class OContextHelper_Impl;
193             friend class OExternalLockGuard;
194         private:
OAccessControlcomphelper::OAccessibleContextHelper::OAccessControl195             OAccessControl() { }
196         };
197 
198         // ensures that the object is alive
199         inline  void            ensureAlive( const OAccessControl& ) const SAL_THROW( ( ::com::sun::star::lang::DisposedException ) );
200         inline  IMutex*         getExternalLock( const OAccessControl& );
201         inline  ::osl::Mutex&   GetMutex( const OAccessControl& );
202 
203     protected:
204         // OComponentHelper
205         virtual void SAL_CALL disposing();
206 
207     protected:
208         // helper
209         /** notifies all AccessibleEventListeners of a certain event
210 
211         @precond    not too be called with our mutex locked
212         @param  _nEventId
213             the id of the even. See AccessibleEventType
214         @param  _rOldValue
215             the old value to be notified
216         @param  _rNewValue
217             the new value to be notified
218         */
219         virtual void SAL_CALL   NotifyAccessibleEvent(
220                     const sal_Int16 _nEventId,
221                     const ::com::sun::star::uno::Any& _rOldValue,
222                     const ::com::sun::star::uno::Any& _rNewValue
223                 );
224 
225         /** records a certain event so that all AccessibleEventListeners can
226             be notified later on.
227 
228             Can even be called with our mutex locked.
229 
230         @param  _nEventId
231             the id of the even. See AccessibleEventType
232         @param  _rOldValue
233             the old value to be notified
234         @param  _rNewValue
235             the new value to be notified
236         @param  _rBuffer
237             the buffer that records the event
238         */
239         virtual void SAL_CALL   BufferAccessibleEvent(
240                     const sal_Int16 _nEventId,
241                     const ::com::sun::star::uno::Any& _rOldValue,
242                     const ::com::sun::star::uno::Any& _rNewValue,
243                     AccessibleEventBuffer & _rBuffer
244                 );
245 
246         // life time control
247         /// checks whether the object is alive (returns <TRUE/> then) or disposed
248         sal_Bool    isAlive() const;
249         /// checks for beeing alive. If the object is already disposed (i.e. not alive), an exception is thrown.
250         void        ensureAlive() const SAL_THROW( ( ::com::sun::star::lang::DisposedException ) );
251 
252         /** ensures that the object is disposed.
253         @precond
254             to be called from within the destructor of your derived class only!
255         */
256         void        ensureDisposed( );
257 
258         /** shortcut for retrieving the context of the parent (returned by getAccessibleParent)
259         */
260         ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >
261                     implGetParentContext() SAL_THROW( ( ::com::sun::star::uno::RuntimeException ) );
262 
263         // access to the base class' broadcast helper/mutex
GetBroadcastHelper()264         ::cppu::OBroadcastHelper&       GetBroadcastHelper()        { return rBHelper; }
GetBroadcastHelper() const265         const ::cppu::OBroadcastHelper& GetBroadcastHelper() const  { return rBHelper; }
GetMutex()266         ::osl::Mutex&                   GetMutex()                  { return m_aMutex; }
267         IMutex*                         getExternalLock( );
268     };
269 
270     //---------------------------------------------------------------------
ensureAlive(const OAccessControl &) const271     inline  void OAccessibleContextHelper::ensureAlive( const OAccessControl& ) const SAL_THROW( ( ::com::sun::star::lang::DisposedException ) )
272     {
273         ensureAlive();
274     }
275 
276     //---------------------------------------------------------------------
getExternalLock(const OAccessControl &)277     inline  IMutex* OAccessibleContextHelper::getExternalLock( const OAccessControl& )
278     {
279         return getExternalLock();
280     }
281 
282     //---------------------------------------------------------------------
GetMutex(const OAccessControl &)283     inline  ::osl::Mutex& OAccessibleContextHelper::GetMutex( const OAccessControl& )
284     {
285         return GetMutex();
286     }
287 
288     //=====================================================================
289     //= OContextEntryGuard
290     //=====================================================================
291     typedef ::osl::ClearableMutexGuard  OContextEntryGuard_Base;
292     /** helper class for guarding the entry into OAccessibleContextHelper methods.
293 
294         <p>The class has two responsibilities:
295         <ul><li>it locks the mutex of an OAccessibleContextHelper instance, as long as the guard lives</li>
296             <li>it checks if an given OAccessibleContextHelper instance is alive, else an exception is thrown
297                 our of the constructor of the guard</li>
298         </ul>
299         <br/>
300         This makes it your first choice (hopefully :) for guarding any interface method implementations of
301         you derived class.
302         </p>
303     */
304     class OContextEntryGuard : public OContextEntryGuard_Base
305     {
306     public:
307         /** constructs the guard
308 
309             <p>The given context (it's mutex, respectively) is locked, and an exception is thrown if the context
310             is not alive anymore. In the latter case, of course, the mutex is freed, again.</p>
311 
312         @param _pContext
313             the context which shall be guarded
314         @precond <arg>_pContext</arg> != NULL
315         */
316         inline OContextEntryGuard( OAccessibleContextHelper* _pContext );
317 
318         /** destructs the guard.
319             <p>The context (it's mutex, respectively) is unlocked.</p>
320         */
321         inline ~OContextEntryGuard();
322     };
323 
324     //.....................................................................
OContextEntryGuard(OAccessibleContextHelper * _pContext)325     inline OContextEntryGuard::OContextEntryGuard( OAccessibleContextHelper* _pContext  )
326         :OContextEntryGuard_Base( _pContext->GetMutex( OAccessibleContextHelper::OAccessControl() ) )
327     {
328         _pContext->ensureAlive( OAccessibleContextHelper::OAccessControl() );
329     }
330 
331     //.....................................................................
~OContextEntryGuard()332     inline OContextEntryGuard::~OContextEntryGuard()
333     {
334     }
335 
336     //=====================================================================
337     //= OExternalLockGuard
338     //=====================================================================
339     class OExternalLockGuard
340             :public OMutexGuard
341             ,public OContextEntryGuard
342     {
343     public:
344         inline OExternalLockGuard( OAccessibleContextHelper* _pContext );
345         inline ~OExternalLockGuard( );
346     };
347 
348     //.....................................................................
OExternalLockGuard(OAccessibleContextHelper * _pContext)349     inline OExternalLockGuard::OExternalLockGuard( OAccessibleContextHelper* _pContext )
350         :OMutexGuard( _pContext->getExternalLock( OAccessibleContextHelper::OAccessControl() ) )
351         ,OContextEntryGuard( _pContext )
352     {
353         // #102438#
354         // Only lock the external mutex,
355         // release the ::osl::Mutex of the OAccessibleContextHelper instance.
356         // If you call into another UNO object with locked ::osl::Mutex,
357         // this may lead to dead locks.
358         clear();
359     }
360 
361     //.....................................................................
~OExternalLockGuard()362     inline OExternalLockGuard::~OExternalLockGuard( )
363     {
364     }
365 
366 //.........................................................................
367 }   // namespace comphelper
368 //.........................................................................
369 
370 #endif // COMPHELPER_ACCESSIBLE_CONTEXT_HELPER_HXX
371 
372 
373