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 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_comphelper.hxx"
30 #include <comphelper/accessibleeventnotifier.hxx>
31 #include <osl/diagnose.h>
32 #include <rtl/instance.hxx>
33 #include <comphelper/guarding.hxx>
34 
35 using namespace ::com::sun::star::uno;
36 using namespace ::com::sun::star::lang;
37 using namespace ::com::sun::star::accessibility;
38 using namespace ::comphelper;
39 
40 //=====================================================================
41 //= AccessibleEventNotifier
42 //=====================================================================
43 //---------------------------------------------------------------------
44 namespace
45 {
46     struct lclMutex
47         : public rtl::Static< ::osl::Mutex, lclMutex > {};
48     struct Clients
49         : public rtl::Static< AccessibleEventNotifier::ClientMap, Clients > {};
50 }
51 
52 //.........................................................................
53 namespace comphelper
54 {
55 //.........................................................................
56 
57 	//---------------------------------------------------------------------
58 	AccessibleEventNotifier::TClientId AccessibleEventNotifier::generateId()
59 	{
60 		TClientId nBiggestUsedId = 0;
61 		TClientId nFreeId = 0;
62 
63 		// look through all registered clients until we find a "gap" in the ids
64 
65 		// Note that the following relies on the fact the elements in the map are traveled with
66 		// ascending keys (aka client ids)
67         AccessibleEventNotifier::ClientMap &rClients = Clients::get();
68 		for	(	ClientMap::const_iterator aLookup = rClients.begin();
69 				aLookup != rClients.end();
70 				++aLookup
71 			)
72 		{
73 			TClientId nCurrent = aLookup->first;
74 			OSL_ENSURE( nCurrent > nBiggestUsedId, "AccessibleEventNotifier::generateId: map is expected to be sorted ascending!" );
75 
76 			if ( nCurrent - nBiggestUsedId > 1 )
77 			{	// found a "gap"
78 				nFreeId = nBiggestUsedId + 1;
79 				break;
80 			}
81 
82 			nBiggestUsedId = nCurrent;
83 		}
84 
85 		if ( !nFreeId )
86 			nFreeId = nBiggestUsedId + 1;
87 
88 		OSL_ENSURE( rClients.end() == rClients.find( nFreeId ),
89 			"AccessibleEventNotifier::generateId: algorithm broken!" );
90 
91 		return nFreeId;
92 	}
93 
94 	//---------------------------------------------------------------------
95 	AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient( )
96 	{
97 		::osl::MutexGuard aGuard( lclMutex::get() );
98 
99 		// generate a new client id
100 		TClientId nNewClientId = generateId( );
101 
102 		// the event listeners for the new client
103 		EventListeners* pNewListeners = new EventListeners( lclMutex::get() );
104 			// note that we're using our own mutex here, so the listener containers for all
105 			// our clients share this same mutex.
106 			// this is a reminiscense to the days where the notifier was asynchronous. Today this is
107 			// completely nonsense, and potentially slowing down the Office me thinks ...
108 
109 		// add the client
110 		Clients::get().insert( ClientMap::value_type( nNewClientId, pNewListeners ) );
111 
112 		// outta here
113 		return nNewClientId;
114 	}
115 
116 	//---------------------------------------------------------------------
117 	sal_Bool AccessibleEventNotifier::implLookupClient( const TClientId _nClient, ClientMap::iterator& _rPos )
118 	{
119 		// look up this client
120         AccessibleEventNotifier::ClientMap &rClients = Clients::get();
121 		_rPos = rClients.find( _nClient );
122 		OSL_ENSURE( rClients.end() != _rPos, "AccessibleEventNotifier::implLookupClient: invalid client id (did you register your client?)!" );
123 
124 		return ( rClients.end() != _rPos );
125 	}
126 
127 	//---------------------------------------------------------------------
128 	void AccessibleEventNotifier::revokeClient( const TClientId _nClient )
129 	{
130 		::osl::MutexGuard aGuard( lclMutex::get() );
131 
132 		ClientMap::iterator aClientPos;
133 		if ( !implLookupClient( _nClient, aClientPos ) )
134 			// already asserted in implLookupClient
135 			return;
136 
137 		// remove it from the clients map
138 		delete aClientPos->second;
139 		Clients::get().erase( aClientPos );
140 	}
141 
142 	//---------------------------------------------------------------------
143 	void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient,
144 			const Reference< XInterface >& _rxEventSource ) SAL_THROW( ( ) )
145 	{
146 		::osl::MutexGuard aGuard( lclMutex::get() );
147 
148 		ClientMap::iterator aClientPos;
149 		if ( !implLookupClient( _nClient, aClientPos ) )
150 			// already asserted in implLookupClient
151 			return;
152 
153 		// notify the "disposing" event for this client
154 		EventObject aDisposalEvent;
155 		aDisposalEvent.Source = _rxEventSource;
156 
157 		// notify the listeners
158 		EventListeners* pListeners = aClientPos->second;
159 
160 		// we do not need the entry in the clients map anymore
161 		// (do this before actually notifying, because some client implementations have re-entrance
162 		// problems and call into revokeClient while we are notifying from hereing)
163 		Clients::get().erase( aClientPos );
164 
165 		// now really do the notification
166 		pListeners->disposeAndClear( aDisposalEvent );
167 		delete pListeners;
168 
169 	}
170 
171 	//---------------------------------------------------------------------
172 	sal_Int32 AccessibleEventNotifier::addEventListener(
173 		const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
174 	{
175 		::osl::MutexGuard aGuard( lclMutex::get() );
176 
177 		ClientMap::iterator aClientPos;
178 		if ( !implLookupClient( _nClient, aClientPos ) )
179 			// already asserted in implLookupClient
180 			return 0;
181 
182 		if ( _rxListener.is() )
183 			aClientPos->second->addInterface( _rxListener );
184 
185 		return aClientPos->second->getLength();
186 	}
187 
188 	//---------------------------------------------------------------------
189 	sal_Int32 AccessibleEventNotifier::removeEventListener(
190 		const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
191 	{
192 		::osl::MutexGuard aGuard( lclMutex::get() );
193 
194 		ClientMap::iterator aClientPos;
195 		if ( !implLookupClient( _nClient, aClientPos ) )
196 			// already asserted in implLookupClient
197 			return 0;
198 
199 		if ( _rxListener.is() )
200 			aClientPos->second->removeInterface( _rxListener );
201 
202 		return aClientPos->second->getLength();
203 	}
204 
205 	//---------------------------------------------------------------------
206 	Sequence< Reference< XInterface > > AccessibleEventNotifier::getEventListeners( const TClientId _nClient ) SAL_THROW( ( ) )
207 	{
208 		Sequence< Reference< XInterface > > aListeners;
209 
210 		::osl::MutexGuard aGuard( lclMutex::get() );
211 
212 		ClientMap::iterator aClientPos;
213 		if ( implLookupClient( _nClient, aClientPos ) )
214 			aListeners = aClientPos->second->getElements();
215 
216 		return aListeners;
217 	}
218 
219 	//---------------------------------------------------------------------
220 	void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) SAL_THROW( ( ) )
221 	{
222 		Sequence< Reference< XInterface > > aListeners;
223 
224 		// --- <mutex lock> -------------------------------
225 		{
226 			::osl::MutexGuard aGuard( lclMutex::get() );
227 
228 			ClientMap::iterator aClientPos;
229 			if ( !implLookupClient( _nClient, aClientPos ) )
230 				// already asserted in implLookupClient
231 				return;
232 
233 			// since we're synchronous, again, we want to notify immediately
234 			aListeners = aClientPos->second->getElements();
235 		}
236 		// --- </mutex lock> ------------------------------
237 
238 			// default handling: loop through all listeners, and notify them
239 		const Reference< XInterface >* pListeners = aListeners.getConstArray();
240 		const Reference< XInterface >* pListenersEnd = pListeners + aListeners.getLength();
241 		while ( pListeners != pListenersEnd )
242 		{
243 			try
244 			{
245 				static_cast< XAccessibleEventListener* >( pListeners->get() )->notifyEvent( _rEvent );
246 			}
247 			catch( const Exception& )
248 			{
249 				// no assertion, because a broken access remote bridge or something like this
250 				// can cause this exception
251 			}
252 			++pListeners;
253 		}
254 	}
255 
256 //.........................................................................
257 }	// namespace comphelper
258 //.........................................................................
259 
260