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 definitely 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 being 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