Branch data 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 : :
20 : :
21 : : #include "comphelper_module.hxx"
22 : :
23 : : #include <com/sun/star/util/XCloseBroadcaster.hpp>
24 : : #include <com/sun/star/util/XCloseable.hpp>
25 : : #include <com/sun/star/lang/DisposedException.hpp>
26 : : #include <com/sun/star/lang/IllegalArgumentException.hpp>
27 : : #include <com/sun/star/frame/XDesktop.hpp>
28 : : #include <com/sun/star/frame/DoubleInitializationException.hpp>
29 : : #include <com/sun/star/beans/XPropertySet.hpp>
30 : :
31 : : #include "instancelocker.hxx"
32 : :
33 : : using namespace ::com::sun::star;
34 : :
35 : :
36 : : // ====================================================================
37 : : // OInstanceLocker
38 : : // ====================================================================
39 : :
40 : : // --------------------------------------------------------
41 : 0 : OInstanceLocker::OInstanceLocker( const uno::Reference< uno::XComponentContext >& xContext )
42 : : : m_xContext( xContext )
43 : : , m_pLockListener( NULL )
44 : : , m_pListenersContainer( NULL )
45 : : , m_bDisposed( sal_False )
46 [ # # ]: 0 : , m_bInitialized( sal_False )
47 : : {
48 : 0 : }
49 : :
50 : : // --------------------------------------------------------
51 [ # # ]: 0 : OInstanceLocker::~OInstanceLocker()
52 : : {
53 [ # # ]: 0 : if ( !m_bDisposed )
54 : : {
55 : 0 : m_refCount++; // to call dispose
56 : : try {
57 [ # # ]: 0 : dispose();
58 : : }
59 [ # # ]: 0 : catch ( uno::RuntimeException& )
60 : : {}
61 : : }
62 : :
63 [ # # ]: 0 : if ( m_pListenersContainer )
64 : : {
65 [ # # ][ # # ]: 0 : delete m_pListenersContainer;
66 : 0 : m_pListenersContainer = NULL;
67 : : }
68 [ # # ][ # # ]: 0 : }
69 : :
70 : : // XComponent
71 : : // --------------------------------------------------------
72 : 0 : void SAL_CALL OInstanceLocker::dispose()
73 : : throw (uno::RuntimeException)
74 : : {
75 [ # # ]: 0 : ::osl::MutexGuard aGuard( m_aMutex );
76 : :
77 [ # # ]: 0 : if ( m_bDisposed )
78 [ # # ]: 0 : throw lang::DisposedException();
79 : :
80 [ # # ][ # # ]: 0 : lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
81 [ # # ]: 0 : if ( m_pListenersContainer )
82 [ # # ]: 0 : m_pListenersContainer->disposeAndClear( aSource );
83 : :
84 [ # # ]: 0 : if ( m_xLockListener.is() )
85 : : {
86 [ # # ]: 0 : if ( m_pLockListener )
87 : : {
88 [ # # ]: 0 : m_pLockListener->Dispose();
89 : 0 : m_pLockListener = NULL;
90 : : }
91 [ # # ]: 0 : m_xLockListener = uno::Reference< uno::XInterface >();
92 : : }
93 : :
94 [ # # ][ # # ]: 0 : m_bDisposed = sal_True;
95 : 0 : }
96 : :
97 : : // --------------------------------------------------------
98 : 0 : void SAL_CALL OInstanceLocker::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
99 : : throw (uno::RuntimeException)
100 : : {
101 [ # # ]: 0 : ::osl::MutexGuard aGuard( m_aMutex );
102 [ # # ]: 0 : if ( m_bDisposed )
103 [ # # ]: 0 : throw lang::DisposedException(); // TODO
104 : :
105 [ # # ]: 0 : if ( !m_pListenersContainer )
106 [ # # ]: 0 : m_pListenersContainer = new ::cppu::OInterfaceContainerHelper( m_aMutex );
107 : :
108 [ # # ][ # # ]: 0 : m_pListenersContainer->addInterface( xListener );
109 : 0 : }
110 : :
111 : : // --------------------------------------------------------
112 : 0 : void SAL_CALL OInstanceLocker::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
113 : : throw (uno::RuntimeException)
114 : : {
115 [ # # ]: 0 : ::osl::MutexGuard aGuard( m_aMutex );
116 [ # # ]: 0 : if ( m_pListenersContainer )
117 [ # # ][ # # ]: 0 : m_pListenersContainer->removeInterface( xListener );
118 : 0 : }
119 : :
120 : : // XInitialization
121 : : // --------------------------------------------------------
122 : 0 : void SAL_CALL OInstanceLocker::initialize( const uno::Sequence< uno::Any >& aArguments )
123 : : throw (uno::Exception, uno::RuntimeException)
124 : : {
125 [ # # ]: 0 : ::osl::MutexGuard aGuard( m_aMutex );
126 [ # # ]: 0 : if ( m_bInitialized )
127 [ # # ]: 0 : throw frame::DoubleInitializationException();
128 : :
129 [ # # ]: 0 : if ( m_bDisposed )
130 [ # # ]: 0 : throw lang::DisposedException(); // TODO
131 : :
132 [ # # ]: 0 : if ( !m_refCount )
133 [ # # ]: 0 : throw uno::RuntimeException(); // the object must be refcounted already!
134 : :
135 : 0 : uno::Reference< uno::XInterface > xInstance;
136 : 0 : uno::Reference< embed::XActionsApproval > xApproval;
137 : 0 : sal_Int32 nModes = 0;
138 : :
139 : : try
140 : : {
141 : 0 : sal_Int32 nLen = aArguments.getLength();
142 [ # # ][ # # ]: 0 : if ( nLen < 2 || nLen > 3 )
143 : : throw lang::IllegalArgumentException(
144 : : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Wrong count of parameters!" ) ),
145 : : uno::Reference< uno::XInterface >(),
146 [ # # ][ # # ]: 0 : 0 );
147 : :
148 [ # # ][ # # ]: 0 : if ( !( aArguments[0] >>= xInstance ) || !xInstance.is() )
[ # # ][ # # ]
149 : : throw lang::IllegalArgumentException(
150 : : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Nonempty reference is expected as the first argument!" ) ),
151 : : uno::Reference< uno::XInterface >(),
152 [ # # ][ # # ]: 0 : 0 );
153 : :
154 [ # # ][ # # ]: 0 : if (
[ # # ][ # # ]
155 : 0 : !( aArguments[1] >>= nModes ) ||
156 : : (
157 : 0 : !( nModes & embed::Actions::PREVENT_CLOSE ) &&
158 : 0 : !( nModes & embed::Actions::PREVENT_TERMINATION )
159 : : )
160 : : )
161 : : {
162 : : throw lang::IllegalArgumentException(
163 : : ::rtl::OUString(
164 : : RTL_CONSTASCII_USTRINGPARAM("The correct modes set is expected as the second argument!" ) ),
165 : : uno::Reference< uno::XInterface >(),
166 [ # # ][ # # ]: 0 : 0 );
167 : : }
168 : :
169 [ # # ][ # # ]: 0 : if ( nLen == 3 && !( aArguments[2] >>= xApproval ) )
[ # # ][ # # ]
170 : : throw lang::IllegalArgumentException(
171 : : ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("If the third argument is provided, it must be XActionsApproval implementation!" ) ),
172 : : uno::Reference< uno::XInterface >(),
173 [ # # ][ # # ]: 0 : 0 );
174 : :
175 : : m_pLockListener = new OLockListener( uno::Reference< lang::XComponent > ( static_cast< lang::XComponent* >( this ) ),
176 : : xInstance,
177 : : nModes,
178 [ # # ][ # # ]: 0 : xApproval );
[ # # ][ # # ]
179 [ # # ][ # # ]: 0 : m_xLockListener = uno::Reference< uno::XInterface >( static_cast< OWeakObject* >( m_pLockListener ) );
180 [ # # ]: 0 : m_pLockListener->Init();
181 : : }
182 [ # # ]: 0 : catch( uno::Exception& )
183 : : {
184 [ # # ]: 0 : dispose();
185 : 0 : throw;
186 : : }
187 : :
188 [ # # ]: 0 : m_bInitialized = sal_True;
189 : 0 : }
190 : :
191 : :
192 : : // XServiceInfo
193 : : // --------------------------------------------------------
194 : 0 : ::rtl::OUString SAL_CALL OInstanceLocker::getImplementationName( )
195 : : throw (uno::RuntimeException)
196 : : {
197 : 0 : return getImplementationName_static();
198 : : }
199 : :
200 : : // --------------------------------------------------------
201 : 0 : ::sal_Bool SAL_CALL OInstanceLocker::supportsService( const ::rtl::OUString& ServiceName )
202 : : throw (uno::RuntimeException)
203 : : {
204 [ # # ]: 0 : uno::Sequence< ::rtl::OUString > aSeq = getSupportedServiceNames();
205 : :
206 [ # # ]: 0 : for ( sal_Int32 nInd = 0; nInd < aSeq.getLength(); nInd++ )
207 [ # # ][ # # ]: 0 : if ( ServiceName.compareTo( aSeq[nInd] ) == 0 )
208 : 0 : return sal_True;
209 : :
210 [ # # ]: 0 : return sal_False;
211 : : }
212 : :
213 : : // --------------------------------------------------------
214 : 0 : uno::Sequence< ::rtl::OUString > SAL_CALL OInstanceLocker::getSupportedServiceNames()
215 : : throw (uno::RuntimeException)
216 : : {
217 : 0 : return getSupportedServiceNames_static();
218 : : }
219 : :
220 : : // Static methods
221 : : // --------------------------------------------------------
222 : 227 : uno::Sequence< ::rtl::OUString > SAL_CALL OInstanceLocker::getSupportedServiceNames_static()
223 : : {
224 [ + - ]: 227 : const rtl::OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.embed.InstanceLocker" ) );
225 [ + - ]: 227 : return uno::Sequence< rtl::OUString >( &aServiceName, 1 );
226 : : }
227 : :
228 : : // --------------------------------------------------------
229 : 227 : ::rtl::OUString SAL_CALL OInstanceLocker::getImplementationName_static()
230 : : {
231 : 227 : return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.embed.InstanceLocker" ) );
232 : : }
233 : :
234 : : // --------------------------------------------------------
235 : 0 : uno::Reference< uno::XInterface > SAL_CALL OInstanceLocker::Create(
236 : : const uno::Reference< uno::XComponentContext >& rxContext )
237 : : {
238 [ # # ]: 0 : return static_cast< cppu::OWeakObject * >( new OInstanceLocker( rxContext ) );
239 : : }
240 : :
241 : :
242 : :
243 : : // ====================================================================
244 : : // OLockListener
245 : : // ====================================================================
246 : :
247 : : // --------------------------------------------------------
248 : 0 : OLockListener::OLockListener( const uno::WeakReference< lang::XComponent >& xWrapper,
249 : : const uno::Reference< uno::XInterface >& xInstance,
250 : : sal_Int32 nMode,
251 : : const uno::Reference< embed::XActionsApproval > xApproval )
252 : : : m_xInstance( xInstance )
253 : : , m_xApproval( xApproval )
254 : : , m_xWrapper( xWrapper )
255 : : , m_bDisposed( sal_False )
256 : : , m_bInitialized( sal_False )
257 [ # # ][ # # ]: 0 : , m_nMode( nMode )
258 : : {
259 : 0 : }
260 : :
261 : : // --------------------------------------------------------
262 [ # # ][ # # ]: 0 : OLockListener::~OLockListener()
263 : : {
264 [ # # ]: 0 : }
265 : :
266 : : // --------------------------------------------------------
267 : 0 : void OLockListener::Dispose()
268 : : {
269 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
270 : :
271 [ # # ]: 0 : if ( m_bDisposed )
272 : 0 : return;
273 : :
274 [ # # ]: 0 : if ( m_nMode & embed::Actions::PREVENT_CLOSE )
275 : : {
276 : : try
277 : : {
278 [ # # ]: 0 : uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY );
279 [ # # ]: 0 : if ( xCloseBroadcaster.is() )
280 [ # # ][ # # ]: 0 : xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
[ # # ]
281 : :
282 [ # # ]: 0 : uno::Reference< util::XCloseable > xCloseable( m_xInstance, uno::UNO_QUERY );
283 [ # # ]: 0 : if ( xCloseable.is() )
284 [ # # ][ # # ]: 0 : xCloseable->close( sal_True );
[ # # ]
285 : : }
286 [ # # ]: 0 : catch( uno::Exception& )
287 : : {}
288 : : }
289 : :
290 [ # # ]: 0 : if ( m_nMode & embed::Actions::PREVENT_TERMINATION )
291 : : {
292 : : try
293 : : {
294 [ # # ]: 0 : uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW );
295 [ # # ][ # # ]: 0 : xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
[ # # ][ # # ]
296 : : }
297 [ # # ]: 0 : catch( uno::Exception& )
298 : : {}
299 : : }
300 : :
301 [ # # ]: 0 : m_xInstance = uno::Reference< uno::XInterface >();
302 [ # # ][ # # ]: 0 : m_bDisposed = sal_True;
303 : : }
304 : :
305 : : // XEventListener
306 : : // --------------------------------------------------------
307 : 0 : void SAL_CALL OLockListener::disposing( const lang::EventObject& aEvent )
308 : : throw (uno::RuntimeException)
309 : : {
310 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
311 : :
312 : : // object is disposed
313 [ # # ][ # # ]: 0 : if ( aEvent.Source == m_xInstance )
314 : : {
315 : : // the object does not listen for anything any more
316 : 0 : m_nMode = 0;
317 : :
318 : : // dispose the wrapper;
319 [ # # ][ # # ]: 0 : uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
[ # # ]
320 [ # # ]: 0 : aGuard.clear();
321 [ # # ]: 0 : if ( xComponent.is() )
322 : : {
323 [ # # ][ # # ]: 0 : try { xComponent->dispose(); }
324 [ # # ]: 0 : catch( uno::Exception& ){}
325 : 0 : }
326 [ # # ]: 0 : }
327 : 0 : }
328 : :
329 : :
330 : : // XCloseListener
331 : : // --------------------------------------------------------
332 : 0 : void SAL_CALL OLockListener::queryClosing( const lang::EventObject& aEvent, sal_Bool )
333 : : throw (util::CloseVetoException, uno::RuntimeException)
334 : : {
335 : : // GetsOwnership parameter is always ignored, the user of the service must close the object always
336 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
337 [ # # ][ # # ]: 0 : if ( !m_bDisposed && aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_CLOSE ) )
[ # # ][ # # ]
[ # # ]
338 : : {
339 : : try
340 : : {
341 : 0 : uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
342 : :
343 : : // unlock the mutex here
344 [ # # ]: 0 : aGuard.clear();
345 : :
346 [ # # ][ # # ]: 0 : if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_CLOSE ) )
[ # # ][ # # ]
[ # # ]
347 [ # # ]: 0 : throw util::CloseVetoException();
348 : : }
349 [ # # # ]: 0 : catch( util::CloseVetoException& )
350 : : {
351 : : // rethrow this exception
352 : 0 : throw;
353 : : }
354 [ # # ]: 0 : catch( uno::Exception& )
355 : : {
356 : : // no action should be done
357 : : }
358 [ # # ]: 0 : }
359 : 0 : }
360 : :
361 : : // --------------------------------------------------------
362 : 0 : void SAL_CALL OLockListener::notifyClosing( const lang::EventObject& aEvent )
363 : : throw (uno::RuntimeException)
364 : : {
365 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
366 : :
367 : : // object is closed, no reason to listen
368 [ # # ][ # # ]: 0 : if ( aEvent.Source == m_xInstance )
369 : : {
370 [ # # ]: 0 : uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( aEvent.Source, uno::UNO_QUERY );
371 [ # # ]: 0 : if ( xCloseBroadcaster.is() )
372 : : {
373 [ # # ][ # # ]: 0 : xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
[ # # ]
374 : 0 : m_nMode &= ~embed::Actions::PREVENT_CLOSE;
375 [ # # ]: 0 : if ( !m_nMode )
376 : : {
377 : : // dispose the wrapper;
378 [ # # ][ # # ]: 0 : uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
[ # # ]
379 [ # # ]: 0 : aGuard.clear();
380 [ # # ]: 0 : if ( xComponent.is() )
381 : : {
382 [ # # ][ # # ]: 0 : try { xComponent->dispose(); }
383 [ # # ]: 0 : catch( uno::Exception& ){}
384 : 0 : }
385 : : }
386 : 0 : }
387 [ # # ]: 0 : }
388 : 0 : }
389 : :
390 : :
391 : : // XTerminateListener
392 : : // --------------------------------------------------------
393 : 0 : void SAL_CALL OLockListener::queryTermination( const lang::EventObject& aEvent )
394 : : throw (frame::TerminationVetoException, uno::RuntimeException)
395 : : {
396 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
397 [ # # ][ # # ]: 0 : if ( aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_TERMINATION ) )
[ # # ][ # # ]
398 : : {
399 : : try
400 : : {
401 : 0 : uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
402 : :
403 : : // unlock the mutex here
404 [ # # ]: 0 : aGuard.clear();
405 : :
406 [ # # ][ # # ]: 0 : if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_TERMINATION ) )
[ # # ][ # # ]
[ # # ]
407 [ # # ]: 0 : throw frame::TerminationVetoException();
408 : : }
409 [ # # # ]: 0 : catch( frame::TerminationVetoException& )
410 : : {
411 : : // rethrow this exception
412 : 0 : throw;
413 : : }
414 [ # # ]: 0 : catch( uno::Exception& )
415 : : {
416 : : // no action should be done
417 : : }
418 [ # # ]: 0 : }
419 : 0 : }
420 : :
421 : : // --------------------------------------------------------
422 : 0 : void SAL_CALL OLockListener::notifyTermination( const lang::EventObject& aEvent )
423 : : throw (uno::RuntimeException)
424 : : {
425 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
426 : :
427 : : // object is terminated, no reason to listen
428 [ # # ][ # # ]: 0 : if ( aEvent.Source == m_xInstance )
429 : : {
430 [ # # ]: 0 : uno::Reference< frame::XDesktop > xDesktop( aEvent.Source, uno::UNO_QUERY );
431 [ # # ]: 0 : if ( xDesktop.is() )
432 : : {
433 : : try
434 : : {
435 [ # # ][ # # ]: 0 : xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
[ # # ]
436 : 0 : m_nMode &= ~embed::Actions::PREVENT_TERMINATION;
437 [ # # ]: 0 : if ( !m_nMode )
438 : : {
439 : : // dispose the wrapper;
440 [ # # ][ # # ]: 0 : uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
[ # # ]
441 [ # # ]: 0 : aGuard.clear();
442 [ # # ]: 0 : if ( xComponent.is() )
443 : : {
444 [ # # ][ # # ]: 0 : try { xComponent->dispose(); }
445 [ # # ]: 0 : catch( uno::Exception& ){}
446 [ # # ]: 0 : }
447 : : }
448 : : }
449 [ # # ]: 0 : catch( uno::Exception& )
450 : : {}
451 : 0 : }
452 [ # # ]: 0 : }
453 : 0 : }
454 : :
455 : :
456 : : // XInitialization
457 : : // --------------------------------------------------------
458 : 0 : sal_Bool OLockListener::Init()
459 : : {
460 [ # # ]: 0 : ::osl::ResettableMutexGuard aGuard( m_aMutex );
461 : :
462 [ # # ][ # # ]: 0 : if ( m_bDisposed || m_bInitialized )
463 : 0 : return sal_False;
464 : :
465 : : try
466 : : {
467 [ # # ]: 0 : if ( m_nMode & embed::Actions::PREVENT_CLOSE )
468 : : {
469 [ # # ]: 0 : uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY_THROW );
470 [ # # ][ # # ]: 0 : xCloseBroadcaster->addCloseListener( static_cast< util::XCloseListener* >( this ) );
[ # # ]
471 : : }
472 : :
473 [ # # ]: 0 : if ( m_nMode & embed::Actions::PREVENT_TERMINATION )
474 : : {
475 [ # # ]: 0 : uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW );
476 [ # # ][ # # ]: 0 : xDesktop->addTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
[ # # ]
477 : : }
478 : : }
479 [ # # ]: 0 : catch( uno::Exception& )
480 : : {
481 : : // dispose the wrapper;
482 [ # # # # : 0 : uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
# # ]
483 [ # # ]: 0 : aGuard.clear();
484 [ # # ]: 0 : if ( xComponent.is() )
485 : : {
486 [ # # # # ]: 0 : try { xComponent->dispose(); }
487 [ # # ]: 0 : catch( uno::Exception& ){}
488 : : }
489 : :
490 : 0 : throw;
491 : : }
492 : :
493 : 0 : m_bInitialized = sal_True;
494 : :
495 [ # # ]: 0 : return sal_True;
496 : : }
497 : :
498 : 227 : void createRegistryInfo_OInstanceLocker()
499 : : {
500 [ + - ][ + - ]: 227 : static ::comphelper::module::OAutoRegistration< OInstanceLocker > aAutoRegistration;
[ + - ][ # # ]
501 : 227 : }
502 : :
503 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|