Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 : #ifndef _CPPUHELPER_INTERFACECONTAINER_H_
20 : #define _CPPUHELPER_INTERFACECONTAINER_H_
21 :
22 : #include <vector>
23 : #include <osl/mutex.hxx>
24 : #include <rtl/alloc.h>
25 : #include <com/sun/star/uno/Sequence.hxx>
26 : #include <com/sun/star/uno/XInterface.hpp>
27 : #ifndef _COM_SUN_STAR_LANG_EVENTOBJECT_HXX_
28 : #include <com/sun/star/lang/EventObject.hpp>
29 : #endif
30 :
31 : #ifndef _COM_SUN_STAR_LANG_DISPOSEDEXCEPTION_HXX_
32 : #include "com/sun/star/lang/DisposedException.hpp"
33 : #endif
34 : #include "cppuhelperdllapi.h"
35 :
36 : /** */ //for docpp
37 : namespace cppu
38 : {
39 :
40 : namespace detail {
41 :
42 : union element_alias
43 : {
44 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > *pAsSequence;
45 : ::com::sun::star::uno::XInterface * pAsInterface;
46 204401 : element_alias() : pAsInterface(0) {}
47 : };
48 :
49 : }
50 :
51 : //===================================================================
52 : class OInterfaceContainerHelper;
53 : /**
54 : This is the iterator of a InterfaceContainerHelper. Typically
55 : one constructs an instance on the stack for one firing session.
56 : It is not allowed to assign or copy an instance of this class.
57 :
58 : @see OInterfaceContainerHelper
59 : */
60 : class CPPUHELPER_DLLPUBLIC OInterfaceIteratorHelper
61 : {
62 : public:
63 : /**
64 : Create an iterator over the elements of the container. The iterator
65 : copies the elements of the conatainer. A change to the container
66 : during the lifetime of an iterator is allowed and does not
67 : affect the iterator-instance. The iterator and the container take cares
68 : themself for concurrent access, no additional guarding is necessary.
69 :
70 : Remark: The copy is on demand. The iterator copy the elements only if the container
71 : change the contents. It is not allowed to destroy the container as long
72 : as an iterator exist.
73 :
74 : @param rCont the container of the elements.
75 : */
76 : OInterfaceIteratorHelper( OInterfaceContainerHelper & rCont ) SAL_THROW(());
77 :
78 : /**
79 : Releases the connection to the container.
80 : */
81 : ~OInterfaceIteratorHelper() SAL_THROW(());
82 :
83 : /** Return sal_True, if there are more elements in the iterator. */
84 190974 : sal_Bool SAL_CALL hasMoreElements() const SAL_THROW(())
85 190974 : { return nRemain != 0; }
86 : /** Return the next element of the iterator. Calling this method if
87 : hasMoreElements() has returned sal_False, is an error. Cast the
88 : returned pointer to the
89 : */
90 : ::com::sun::star::uno::XInterface * SAL_CALL next() SAL_THROW(());
91 :
92 : /** Removes the current element (the last one returned by next())
93 : from the underlying container. Calling this method before
94 : next() has been called or calling it twice with no next()
95 : inbetween is an error.
96 : */
97 : void SAL_CALL remove() SAL_THROW(());
98 :
99 : private:
100 : OInterfaceContainerHelper & rCont;
101 : sal_Bool bIsList;
102 :
103 : detail::element_alias aData;
104 :
105 : sal_Int32 nRemain;
106 :
107 : OInterfaceIteratorHelper( const OInterfaceIteratorHelper & ) SAL_THROW(());
108 : OInterfaceIteratorHelper & operator = ( const OInterfaceIteratorHelper & ) SAL_THROW(());
109 : };
110 :
111 : //===================================================================
112 : /**
113 : A container of interfaces. To access the elements use an iterator.
114 : This implementation is thread save.
115 :
116 : @see OInterfaceIteratorHelper
117 : */
118 : class CPPUHELPER_DLLPUBLIC OInterfaceContainerHelper
119 : {
120 : public:
121 : // these are here to force memory de/allocation to sal lib.
122 22658 : inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW(())
123 22658 : { return ::rtl_allocateMemory( nSize ); }
124 18002 : inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW(())
125 18002 : { ::rtl_freeMemory( pMem ); }
126 : inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW(())
127 : { return pMem; }
128 : inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW(())
129 : {}
130 :
131 : /**
132 : Create an interface container.
133 :
134 : @param rMutex the mutex to protect multi thread access.
135 : The lifetime must be longer than the lifetime
136 : of this object.
137 : */
138 : OInterfaceContainerHelper( ::osl::Mutex & rMutex ) SAL_THROW(());
139 : /**
140 : Release all interfaces. All iterators must be destroyed before
141 : the container is destructed.
142 : */
143 : ~OInterfaceContainerHelper() SAL_THROW(());
144 : /**
145 : Return the number of Elements in the container. Only useful if you have acquired
146 : the mutex.
147 : */
148 : sal_Int32 SAL_CALL getLength() const SAL_THROW(());
149 :
150 : /**
151 : Return all interfaces added to this container.
152 : **/
153 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > SAL_CALL getElements() const SAL_THROW(());
154 :
155 : /** Inserts an element into the container. The position is not specified, thus it is not
156 : specified in which order events are fired.
157 :
158 : @attention
159 : If you add the same interface more than once, then it will be added to the elements list
160 : more than once and thus if you want to remove that interface from the list, you have to call
161 : removeInterface() the same number of times.
162 : In the latter case, you will also get events fired more than once (if the interface is a
163 : listener interface).
164 :
165 : @param rxIFace
166 : interface to be added; it is allowed to insert null or
167 : the same interface more than once
168 : @return
169 : the new count of elements in the container
170 : */
171 : sal_Int32 SAL_CALL addInterface( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace ) SAL_THROW(());
172 : /** Removes an element from the container. It uses interface equality to remove the interface.
173 :
174 : @param rxIFace
175 : interface to be removed
176 : @return
177 : the new count of elements in the container
178 : */
179 : sal_Int32 SAL_CALL removeInterface( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace ) SAL_THROW(());
180 : /**
181 : Call disposing on all object in the container that
182 : support XEventListener. Than clear the container.
183 : */
184 : void SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW(());
185 : /**
186 : Clears the container without calling disposing().
187 : */
188 : void SAL_CALL clear() SAL_THROW(());
189 :
190 : /** Executes a functor for each contained listener of specified type, e.g.
191 : <code>forEach<awt::XPaintListener>(...</code>.
192 :
193 : If a com::sun::star::lang::DisposedException occurs which relates to
194 : the called listener, then that listener is removed from the container.
195 :
196 : @tparam ListenerT listener type
197 : @tparam FuncT unary functor type, let your compiler deduce this for you
198 : @param func unary functor object expecting an argument of type
199 : ::com::sun::star::uno::Reference<ListenerT>
200 : */
201 : template <typename ListenerT, typename FuncT>
202 : inline void forEach( FuncT const& func );
203 :
204 : /** Calls a UNO listener method for each contained listener.
205 :
206 : The listener method must take a single argument of type EventT,
207 : and return <code>void</code>.
208 :
209 : If a com::sun::star::lang::DisposedException occurs which relates to
210 : the called listener, then that listener is removed from the container.
211 :
212 : @tparam ListenerT UNO event listener type, let your compiler deduce this for you
213 : @tparam EventT event type, let your compiler deduce this for you
214 : @param NotificationMethod
215 : Pointer to a method of a ListenerT interface.
216 : @param Event
217 : Event to notify to all contained listeners
218 :
219 : Example:
220 : @code
221 : awt::PaintEvent aEvent( static_cast< cppu::OWeakObject* >( this ), ... );
222 : listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
223 : @endcode
224 : */
225 : template< typename ListenerT, typename EventT >
226 : inline void notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event );
227 :
228 : private:
229 : friend class OInterfaceIteratorHelper;
230 : /**
231 : bIsList == TRUE -> aData.pAsSequence of type Sequence< XInterfaceSequence >,
232 : otherwise aData.pAsInterface == of type (XEventListener *)
233 : */
234 : detail::element_alias aData;
235 : ::osl::Mutex & rMutex;
236 : /** TRUE -> used by an iterator. */
237 : sal_Bool bInUse;
238 : /** TRUE -> aData.pAsSequence is of type Sequence< XInterfaceSequence >. */
239 : sal_Bool bIsList;
240 :
241 : OInterfaceContainerHelper( const OInterfaceContainerHelper & ) SAL_THROW(());
242 : OInterfaceContainerHelper & operator = ( const OInterfaceContainerHelper & ) SAL_THROW(());
243 :
244 : /*
245 : Dulicate content of the conaitner and release the old one without destroying.
246 : The mutex must be locked and the memberbInUse must be true.
247 : */
248 : void copyAndResetInUse() SAL_THROW(());
249 :
250 : private:
251 : template< typename ListenerT, typename EventT >
252 : class NotifySingleListener
253 : {
254 : private:
255 : typedef void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& );
256 : NotificationMethod m_pMethod;
257 : const EventT& m_rEvent;
258 : public:
259 13967 : NotifySingleListener( NotificationMethod method, const EventT& event ) : m_pMethod( method ), m_rEvent( event ) { }
260 :
261 5247 : void operator()( const ::com::sun::star::uno::Reference<ListenerT>& listener ) const
262 : {
263 5247 : (listener.get()->*m_pMethod)( m_rEvent );
264 5247 : }
265 : };
266 : };
267 :
268 : template <typename ListenerT, typename FuncT>
269 25499 : inline void OInterfaceContainerHelper::forEach( FuncT const& func )
270 : {
271 25499 : OInterfaceIteratorHelper iter( *this );
272 71543 : while (iter.hasMoreElements()) {
273 : ::com::sun::star::uno::Reference<ListenerT> const xListener(
274 20545 : iter.next(), ::com::sun::star::uno::UNO_QUERY );
275 20545 : if (xListener.is()) {
276 : #if defined(EXCEPTIONS_OFF)
277 : func( xListener );
278 : #else
279 : try {
280 20545 : func( xListener );
281 : }
282 0 : catch (::com::sun::star::lang::DisposedException const& exc) {
283 0 : if (exc.Context == xListener)
284 46044 : iter.remove();
285 : }
286 : #endif
287 : }
288 : }
289 25499 : }
290 :
291 : template< typename ListenerT, typename EventT >
292 13967 : inline void OInterfaceContainerHelper::notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event )
293 : {
294 13967 : forEach< ListenerT, NotifySingleListener< ListenerT, EventT > >( NotifySingleListener< ListenerT, EventT >( NotificationMethod, Event ) );
295 13967 : }
296 :
297 : //===================================================================
298 : /**
299 : A helper class to store interface references of different types.
300 :
301 : @see OInterfaceIteratorHelper
302 : @see OInterfaceContainerHelper
303 : */
304 : template< class key , class hashImpl , class equalImpl >
305 : class OMultiTypeInterfaceContainerHelperVar
306 : {
307 : public:
308 : // these are here to force memory de/allocation to sal lib.
309 1987 : inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW(())
310 1987 : { return ::rtl_allocateMemory( nSize ); }
311 1987 : inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW(())
312 1987 : { ::rtl_freeMemory( pMem ); }
313 : inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW(())
314 : { return pMem; }
315 : inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW(())
316 : {}
317 :
318 : /**
319 : Create a container of interface containers.
320 :
321 : @param rMutex the mutex to protect multi thread access.
322 : The lifetime must be longer than the lifetime
323 : of this object.
324 : */
325 : inline OMultiTypeInterfaceContainerHelperVar( ::osl::Mutex & rMutex ) SAL_THROW(());
326 : /**
327 : Deletes all containers.
328 : */
329 : inline ~OMultiTypeInterfaceContainerHelperVar() SAL_THROW(());
330 :
331 : /**
332 : Return all id's under which at least one interface is added.
333 : */
334 : inline ::com::sun::star::uno::Sequence< key > SAL_CALL getContainedTypes() const SAL_THROW(());
335 :
336 : /**
337 : Return the container created under this key.
338 : The InterfaceContainerHelper exists until the whole MultiTypeContainer is destroyed.
339 : @return the container created under this key. If the container
340 : was not created, null was returned.
341 : */
342 : inline OInterfaceContainerHelper * SAL_CALL getContainer( const key & ) const SAL_THROW(());
343 :
344 : /** Inserts an element into the container with the specified key.
345 : The position is not specified, thus it is not specified in which order events are fired.
346 :
347 : @attention
348 : If you add the same interface more than once, then it will be added to the elements list
349 : more than once and thus if you want to remove that interface from the list, you have to call
350 : removeInterface() the same number of times.
351 : In the latter case, you will also get events fired more than once (if the interface is a
352 : listener interface).
353 :
354 : @param rKey
355 : the id of the container
356 : @param r
357 : interface to be added; it is allowed, to insert null or
358 : the same interface more than once
359 : @return
360 : the new count of elements in the container
361 : */
362 : inline sal_Int32 SAL_CALL addInterface(
363 : const key & rKey,
364 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & r )
365 : SAL_THROW(());
366 :
367 : /** Removes an element from the container with the specified key.
368 : It uses interface equality to remove the interface.
369 :
370 : @param rKey
371 : the id of the container
372 : @param rxIFace
373 : interface to be removed
374 : @return
375 : the new count of elements in the container
376 : */
377 : inline sal_Int32 SAL_CALL removeInterface(
378 : const key & rKey,
379 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace )
380 : SAL_THROW(());
381 :
382 : /**
383 : Call disposing on all references in the container, that
384 : support XEventListener. Then clears the container.
385 : @param rEvt the event object which is passed during disposing() call
386 : */
387 : inline void SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW(());
388 : /**
389 : Remove all elements of all containers. Does not delete the container.
390 : */
391 : inline void SAL_CALL clear() SAL_THROW(());
392 :
393 : typedef key keyType;
394 : private:
395 : typedef ::std::vector< std::pair < key , void* > > InterfaceMap;
396 : InterfaceMap *m_pMap;
397 : ::osl::Mutex & rMutex;
398 :
399 45482 : inline typename InterfaceMap::iterator find(const key &rKey) const
400 : {
401 45482 : typename InterfaceMap::iterator iter = m_pMap->begin();
402 45482 : typename InterfaceMap::iterator end = m_pMap->end();
403 :
404 45482 : while( iter != end )
405 : {
406 : equalImpl equal;
407 2281 : if( equal( iter->first, rKey ) )
408 : break;
409 245 : iter++;
410 : }
411 45482 : return iter;
412 : }
413 :
414 : inline OMultiTypeInterfaceContainerHelperVar( const OMultiTypeInterfaceContainerHelperVar & ) SAL_THROW(());
415 : inline OMultiTypeInterfaceContainerHelperVar & operator = ( const OMultiTypeInterfaceContainerHelperVar & ) SAL_THROW(());
416 : };
417 :
418 :
419 :
420 :
421 : /**
422 : This struct contains the standard variables of a broadcaster. Helper
423 : classes only know a reference to this struct instead of references
424 : to the four members. The access to the members must be guarded with
425 : rMutex.
426 :
427 : The additional template parameter keyType has been added, because gcc
428 : can't compile addListener( const container::keyType &key ).
429 : */
430 : template < class container , class keyType >
431 27209 : struct OBroadcastHelperVar
432 : {
433 : /** The shared mutex. */
434 : ::osl::Mutex & rMutex;
435 : /** ListenerContainer class is thread safe. */
436 : container aLC;
437 : /** Dispose call ready. */
438 : sal_Bool bDisposed;
439 : /** In dispose call. */
440 : sal_Bool bInDispose;
441 :
442 : /**
443 : Initialize the structur. bDispose and bInDispose are set to false.
444 : @param rMutex_ the mutex reference.
445 : */
446 34462 : OBroadcastHelperVar( ::osl::Mutex & rMutex_ ) SAL_THROW(())
447 : : rMutex( rMutex_ )
448 : , aLC( rMutex_ )
449 : , bDisposed( sal_False )
450 34462 : , bInDispose( sal_False )
451 34462 : {}
452 :
453 : /**
454 : adds a listener threadsafe.
455 : **/
456 8335 : inline void addListener(
457 : const keyType &key,
458 : const ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface > &r )
459 : SAL_THROW(())
460 : {
461 8335 : ::osl::MutexGuard guard( rMutex );
462 : OSL_ENSURE( !bInDispose, "do not add listeners in the dispose call" );
463 : OSL_ENSURE( !bDisposed, "object is disposed" );
464 8335 : if( ! bInDispose && ! bDisposed )
465 8335 : aLC.addInterface( key , r );
466 8335 : }
467 :
468 : /**
469 : removes a listener threadsafe
470 : **/
471 3345 : inline void removeListener(
472 : const keyType &key,
473 : const ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface > & r )
474 : SAL_THROW(())
475 : {
476 3345 : ::osl::MutexGuard guard( rMutex );
477 : OSL_ENSURE( !bDisposed, "object is disposed" );
478 3345 : if( ! bInDispose && ! bDisposed )
479 3290 : aLC.removeInterface( key , r );
480 3345 : }
481 :
482 : /**
483 : Return the container created under this key.
484 : @return the container created under this key. If the container
485 : was not created, null was returned. This can be used to optimize
486 : performance ( construction of an event object can be avoided ).
487 : ***/
488 25206 : inline OInterfaceContainerHelper * SAL_CALL getContainer( const keyType &key ) const SAL_THROW(())
489 25206 : { return aLC.getContainer( key ); }
490 : };
491 :
492 : /*------------------------------------------
493 : *
494 : * In general, the above templates are used with a Type as key.
495 : * Therefore a default declaration is given ( OMultiTypeInterfaceContainerHelper and OBroadcastHelper )
496 : *
497 : *------------------------------------------*/
498 :
499 : // helper function call class
500 : struct hashType_Impl
501 : {
502 : size_t operator()(const ::com::sun::star::uno::Type & s) const SAL_THROW(())
503 : { return (size_t) s.getTypeName().hashCode(); }
504 : };
505 :
506 :
507 : /** Specialized class for key type com::sun::star::uno::Type,
508 : without explicit usage of STL symbols.
509 : */
510 : class CPPUHELPER_DLLPUBLIC OMultiTypeInterfaceContainerHelper
511 : {
512 : public:
513 : // these are here to force memory de/allocation to sal lib.
514 194 : inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW(())
515 194 : { return ::rtl_allocateMemory( nSize ); }
516 117 : inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW(())
517 117 : { ::rtl_freeMemory( pMem ); }
518 : inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW(())
519 : { return pMem; }
520 : inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW(())
521 : {}
522 :
523 : /**
524 : Create a container of interface containers.
525 :
526 : @param rMutex the mutex to protect multi thread access.
527 : The lifetime must be longer than the lifetime
528 : of this object.
529 : */
530 : OMultiTypeInterfaceContainerHelper( ::osl::Mutex & rMutex ) SAL_THROW(());
531 : /**
532 : Delete all containers.
533 : */
534 : ~OMultiTypeInterfaceContainerHelper() SAL_THROW(());
535 :
536 : /**
537 : Return all id's under which at least one interface is added.
538 : */
539 : ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Type > SAL_CALL getContainedTypes() const SAL_THROW(());
540 :
541 : /**
542 : Return the container created under this key.
543 : @return the container created under this key. If the container
544 : was not created, null was returned.
545 : */
546 : OInterfaceContainerHelper * SAL_CALL getContainer( const ::com::sun::star::uno::Type & rKey ) const SAL_THROW(());
547 :
548 : /** Inserts an element into the container with the specified key.
549 : The position is not specified, thus it is not specified in which order events are fired.
550 :
551 : @attention
552 : If you add the same interface more than once, then it will be added to the elements list
553 : more than once and thus if you want to remove that interface from the list, you have to call
554 : removeInterface() the same number of times.
555 : In the latter case, you will also get events fired more than once (if the interface is a
556 : listener interface).
557 :
558 : @param rKey
559 : the id of the container
560 : @param r
561 : interface to be added; it is allowed, to insert null or
562 : the same interface more than once
563 : @return
564 : the new count of elements in the container
565 : */
566 : sal_Int32 SAL_CALL addInterface(
567 : const ::com::sun::star::uno::Type & rKey,
568 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & r )
569 : SAL_THROW(());
570 :
571 : /** Removes an element from the container with the specified key.
572 : It uses interface equality to remove the interface.
573 :
574 : @param rKey
575 : the id of the container
576 : @param rxIFace
577 : interface to be removed
578 : @return
579 : the new count of elements in the container
580 : */
581 : sal_Int32 SAL_CALL removeInterface(
582 : const ::com::sun::star::uno::Type & rKey,
583 : const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace )
584 : SAL_THROW(());
585 :
586 : /**
587 : Call disposing on all object in the container that
588 : support XEventListener. Than clear the container.
589 : */
590 : void SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW(());
591 : /**
592 : Remove all elements of all containers. Does not delete the container.
593 : */
594 : void SAL_CALL clear() SAL_THROW(());
595 :
596 : typedef ::com::sun::star::uno::Type keyType;
597 : private:
598 : void *m_pMap;
599 : ::osl::Mutex & rMutex;
600 :
601 : inline OMultiTypeInterfaceContainerHelper( const OMultiTypeInterfaceContainerHelper & ) SAL_THROW(());
602 : inline OMultiTypeInterfaceContainerHelper & operator = ( const OMultiTypeInterfaceContainerHelper & ) SAL_THROW(());
603 : };
604 :
605 : typedef OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper , OMultiTypeInterfaceContainerHelper::keyType > OBroadcastHelper;
606 :
607 : }
608 :
609 : #endif
610 :
611 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|