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