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><parent>.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