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 : #include <osl/diagnose.h>
21 : #include <osl/mutex.hxx>
22 : #include "rtl/ustrbuf.hxx"
23 : #include <cppuhelper/factory.hxx>
24 : #include <cppuhelper/compbase5.hxx>
25 : #include <cppuhelper/implbase1.hxx>
26 : #include <cppuhelper/implementationentry.hxx>
27 : #include "tdmgr_common.hxx"
28 : #include "tdmgr_tdenumeration.hxx"
29 : #include "lrucache.hxx"
30 :
31 : #include <com/sun/star/lang/XServiceInfo.hpp>
32 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
33 : #include <com/sun/star/lang/XEventListener.hpp>
34 : #include <com/sun/star/lang/XTypeProvider.hpp>
35 : #include <com/sun/star/lang/XComponent.hpp>
36 : #include <com/sun/star/lang/XInitialization.hpp>
37 : #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
38 : #include <com/sun/star/container/XSet.hpp>
39 : #include <com/sun/star/container/XContentEnumerationAccess.hpp>
40 : #include <com/sun/star/reflection/XTypeDescription.hpp>
41 : #include <com/sun/star/reflection/XArrayTypeDescription.hpp>
42 : #include <com/sun/star/reflection/XIndirectTypeDescription.hpp>
43 : #include <com/sun/star/reflection/XInterfaceTypeDescription.hpp>
44 : #include "com/sun/star/reflection/XStructTypeDescription.hpp"
45 : #include <com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp>
46 : #include <com/sun/star/registry/XRegistryKey.hpp>
47 : #include "com/sun/star/uno/RuntimeException.hpp"
48 :
49 : #include <algorithm>
50 : #include <vector>
51 :
52 : using namespace std;
53 : using namespace cppu;
54 : using namespace osl;
55 : using namespace com::sun::star;
56 : using namespace com::sun::star::uno;
57 : using namespace com::sun::star::lang;
58 : using namespace com::sun::star::reflection;
59 : using namespace com::sun::star::container;
60 : using namespace com::sun::star::registry;
61 :
62 : using ::rtl::OUString;
63 : using ::rtl::OUStringBuffer;
64 :
65 : static const sal_Int32 CACHE_SIZE = 512;
66 :
67 : #define SERVICENAME "com.sun.star.reflection.TypeDescriptionManager"
68 : #define IMPLNAME "com.sun.star.comp.stoc.TypeDescriptionManager"
69 :
70 : //--------------------------------------------------------------------------------------------------
71 : // exported via tdmgr_common.hxx
72 : extern rtl_StandardModuleCount g_moduleCount;
73 :
74 : namespace stoc_bootstrap
75 : {
76 518 : Sequence< OUString > SAL_CALL tdmgr_getSupportedServiceNames()
77 : {
78 518 : Sequence< OUString > seqNames(1);
79 518 : seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
80 518 : return seqNames;
81 : }
82 :
83 3368 : OUString SAL_CALL tdmgr_getImplementationName()
84 : {
85 3368 : return OUString(RTL_CONSTASCII_USTRINGPARAM(IMPLNAME));
86 : }
87 : }
88 :
89 : namespace stoc_tdmgr
90 : {
91 : typedef vector< Reference< XHierarchicalNameAccess > > ProviderVector;
92 :
93 : class EnumerationImpl;
94 : class ManagerImpl;
95 :
96 : //==================================================================================================
97 : class EventListenerImpl : public ImplHelper1< XEventListener >
98 : {
99 : ManagerImpl * _pMgr;
100 :
101 : public:
102 259 : EventListenerImpl( ManagerImpl * pMgr )
103 259 : : _pMgr( pMgr )
104 : {
105 259 : ::g_moduleCount.modCnt.acquire( &::g_moduleCount.modCnt );
106 259 : }
107 : virtual ~EventListenerImpl();
108 :
109 : // lifetime delegated to manager
110 : virtual void SAL_CALL acquire() throw();
111 : virtual void SAL_CALL release() throw();
112 :
113 : // XEventListener
114 : virtual void SAL_CALL disposing( const EventObject & rEvt ) throw(::com::sun::star::uno::RuntimeException);
115 : };
116 :
117 48 : EventListenerImpl::~EventListenerImpl()
118 : {
119 24 : ::g_moduleCount.modCnt.release( &::g_moduleCount.modCnt );
120 24 : }
121 :
122 : //==================================================================================================
123 : class ManagerImpl
124 : : public WeakComponentImplHelper5< XServiceInfo,
125 : XSet,
126 : XHierarchicalNameAccess,
127 : XTypeDescriptionEnumerationAccess,
128 : XInitialization >
129 : {
130 : friend class EnumerationImpl;
131 : friend class EventListenerImpl;
132 :
133 : Mutex _aComponentMutex;
134 : Reference< XComponentContext > _xContext;
135 : EventListenerImpl _aEventListener;
136 :
137 : // elements
138 : sal_Bool _bCaching;
139 : LRU_CacheAnyByOUString _aElements;
140 : // provider chain
141 : ProviderVector _aProviders;
142 :
143 : inline Any getSimpleType( const OUString & rName );
144 :
145 : Reference< XTypeDescription > getInstantiatedStruct(OUString const & name);
146 :
147 : protected:
148 : virtual void SAL_CALL disposing();
149 :
150 : public:
151 : ManagerImpl( Reference< XComponentContext > const & xContext, sal_Int32 nCacheSize );
152 : virtual ~ManagerImpl();
153 :
154 : // XInitialization
155 : virtual void SAL_CALL initialize( const Sequence< Any > & args ) throw (Exception, RuntimeException);
156 :
157 : // XServiceInfo
158 : virtual OUString SAL_CALL getImplementationName() throw(::com::sun::star::uno::RuntimeException);
159 : virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) throw(::com::sun::star::uno::RuntimeException);
160 : virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw(::com::sun::star::uno::RuntimeException);
161 :
162 : // XElementAccess
163 : virtual Type SAL_CALL getElementType() throw(::com::sun::star::uno::RuntimeException);
164 : virtual sal_Bool SAL_CALL hasElements() throw(::com::sun::star::uno::RuntimeException);
165 :
166 : // XEnumerationAccess
167 : virtual Reference< XEnumeration > SAL_CALL createEnumeration() throw(::com::sun::star::uno::RuntimeException);
168 :
169 : // XSet
170 : virtual sal_Bool SAL_CALL has( const Any & rElement ) throw(::com::sun::star::uno::RuntimeException);
171 : virtual void SAL_CALL insert( const Any & rElement ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::ElementExistException, ::com::sun::star::uno::RuntimeException);
172 : virtual void SAL_CALL remove( const Any & rElement ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException);
173 :
174 : // XHierarchicalNameAccess
175 : virtual Any SAL_CALL getByHierarchicalName( const OUString & rName ) throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException);
176 : virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
177 :
178 : // XTypeDescriptionEnumerationAccess
179 : virtual ::com::sun::star::uno::Reference<
180 : ::com::sun::star::reflection::XTypeDescriptionEnumeration > SAL_CALL
181 : createTypeDescriptionEnumeration(
182 : const ::rtl::OUString& moduleName,
183 : const ::com::sun::star::uno::Sequence<
184 : ::com::sun::star::uno::TypeClass >& types,
185 : ::com::sun::star::reflection::TypeDescriptionSearchDepth depth )
186 : throw ( ::com::sun::star::reflection::NoSuchTypeNameException,
187 : ::com::sun::star::reflection::InvalidTypeNameException,
188 : ::com::sun::star::uno::RuntimeException );
189 : };
190 :
191 : //==================================================================================================
192 : class EnumerationImpl
193 : : public WeakImplHelper1< XEnumeration >
194 : {
195 : ManagerImpl * _pMgr;
196 : size_t _nPos;
197 :
198 : public:
199 : EnumerationImpl( ManagerImpl * pManager );
200 : virtual ~EnumerationImpl();
201 :
202 : // XEnumeration
203 : virtual sal_Bool SAL_CALL hasMoreElements() throw(::com::sun::star::uno::RuntimeException);
204 : virtual Any SAL_CALL nextElement() throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException);
205 : };
206 :
207 : //##################################################################################################
208 :
209 : // lifetime delegated to manager
210 : //__________________________________________________________________________________________________
211 611 : void EventListenerImpl::acquire() throw()
212 : {
213 611 : _pMgr->acquire();
214 611 : }
215 : //__________________________________________________________________________________________________
216 383 : void EventListenerImpl::release() throw()
217 : {
218 383 : _pMgr->release();
219 383 : }
220 :
221 : // XEventListener
222 : //__________________________________________________________________________________________________
223 31 : void EventListenerImpl::disposing( const EventObject & rEvt )
224 : throw(::com::sun::star::uno::RuntimeException)
225 : {
226 31 : _pMgr->remove( makeAny( rEvt.Source ) );
227 31 : }
228 :
229 : //##################################################################################################
230 :
231 : //__________________________________________________________________________________________________
232 0 : EnumerationImpl::EnumerationImpl( ManagerImpl * pManager )
233 : : _pMgr( pManager )
234 0 : , _nPos( 0 )
235 : {
236 0 : _pMgr->acquire();
237 0 : }
238 : //__________________________________________________________________________________________________
239 0 : EnumerationImpl::~EnumerationImpl()
240 : {
241 0 : _pMgr->release();
242 0 : }
243 :
244 : // XEnumeration
245 : //__________________________________________________________________________________________________
246 0 : sal_Bool EnumerationImpl::hasMoreElements()
247 : throw(::com::sun::star::uno::RuntimeException)
248 : {
249 0 : MutexGuard aGuard( _pMgr->_aComponentMutex );
250 0 : return (_nPos < _pMgr->_aProviders.size());
251 : }
252 : //__________________________________________________________________________________________________
253 0 : Any EnumerationImpl::nextElement()
254 : throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException)
255 : {
256 0 : MutexGuard aGuard( _pMgr->_aComponentMutex );
257 0 : if (_nPos >= _pMgr->_aProviders.size())
258 : {
259 : throw NoSuchElementException(
260 : OUString( RTL_CONSTASCII_USTRINGPARAM("there is no further element!") ),
261 0 : (XWeak *)(OWeakObject *)this );
262 : }
263 0 : return makeAny( _pMgr->_aProviders[_nPos++] );
264 : }
265 :
266 : //##################################################################################################
267 :
268 : //__________________________________________________________________________________________________
269 259 : ManagerImpl::ManagerImpl(
270 : Reference< XComponentContext > const & xContext, sal_Int32 nCacheSize )
271 : : WeakComponentImplHelper5<
272 : XServiceInfo, XSet, XHierarchicalNameAccess,
273 : XTypeDescriptionEnumerationAccess, XInitialization >( _aComponentMutex )
274 : , _xContext( xContext )
275 : , _aEventListener( this )
276 : , _bCaching( sal_True )
277 259 : , _aElements( nCacheSize )
278 : {
279 259 : ::g_moduleCount.modCnt.acquire( &::g_moduleCount.modCnt );
280 259 : }
281 : //__________________________________________________________________________________________________
282 72 : ManagerImpl::~ManagerImpl()
283 : {
284 : OSL_ENSURE( _aProviders.empty(), "### still providers left!" );
285 : OSL_TRACE( "> TypeDescriptionManager shut down. <" );
286 24 : ::g_moduleCount.modCnt.release( &::g_moduleCount.modCnt );
287 48 : }
288 : //__________________________________________________________________________________________________
289 39 : void ManagerImpl::disposing()
290 : {
291 : // called on disposing the tdmgr instance (supposedly from context)
292 39 : _bCaching = sal_False;
293 39 : _aElements.clear();
294 39 : _xContext.clear();
295 39 : _aProviders.clear();
296 39 : }
297 :
298 : // XInitialization
299 : //__________________________________________________________________________________________________
300 0 : void ManagerImpl::initialize(
301 : const Sequence< Any > & args )
302 : throw (Exception, RuntimeException)
303 : {
304 : // additional providers
305 0 : Any const * pProviders = args.getConstArray();
306 0 : for ( sal_Int32 nPos = 0; nPos < args.getLength(); ++nPos )
307 : {
308 0 : Reference< XHierarchicalNameAccess > xHA( pProviders[ nPos ], UNO_QUERY );
309 : OSL_ENSURE( xHA.is(), "### no td provider!" );
310 :
311 0 : if (xHA.is())
312 : {
313 : try
314 : {
315 0 : insert( makeAny( xHA ) );
316 : }
317 0 : catch (IllegalArgumentException &)
318 : {
319 : }
320 0 : catch (ElementExistException &)
321 : {
322 : }
323 : }
324 0 : }
325 0 : }
326 :
327 : // XServiceInfo
328 : //__________________________________________________________________________________________________
329 0 : OUString ManagerImpl::getImplementationName()
330 : throw(::com::sun::star::uno::RuntimeException)
331 : {
332 0 : return stoc_bootstrap::tdmgr_getImplementationName();
333 : }
334 : //__________________________________________________________________________________________________
335 0 : sal_Bool ManagerImpl::supportsService( const OUString & rServiceName )
336 : throw(::com::sun::star::uno::RuntimeException)
337 : {
338 0 : const Sequence< OUString > & rSNL = getSupportedServiceNames();
339 0 : const OUString * pArray = rSNL.getConstArray();
340 0 : for ( sal_Int32 nPos = rSNL.getLength(); nPos--; )
341 : {
342 0 : if (pArray[nPos] == rServiceName)
343 0 : return sal_True;
344 : }
345 0 : return sal_False;
346 : }
347 : //__________________________________________________________________________________________________
348 0 : Sequence< OUString > ManagerImpl::getSupportedServiceNames()
349 : throw(::com::sun::star::uno::RuntimeException)
350 : {
351 0 : return stoc_bootstrap::tdmgr_getSupportedServiceNames();
352 : }
353 :
354 : // XElementAccess
355 : //__________________________________________________________________________________________________
356 0 : Type ManagerImpl::getElementType()
357 : throw(::com::sun::star::uno::RuntimeException)
358 : {
359 0 : return ::getCppuType( (const Reference< XHierarchicalNameAccess > *)0 );
360 : }
361 : //__________________________________________________________________________________________________
362 0 : sal_Bool ManagerImpl::hasElements()
363 : throw(::com::sun::star::uno::RuntimeException)
364 : {
365 0 : MutexGuard aGuard( _aComponentMutex );
366 0 : return (!_aProviders.empty());
367 : }
368 :
369 : // XEnumerationAccess
370 : //__________________________________________________________________________________________________
371 0 : Reference< XEnumeration > ManagerImpl::createEnumeration()
372 : throw(::com::sun::star::uno::RuntimeException)
373 : {
374 0 : return new EnumerationImpl( this );
375 : }
376 :
377 : // XSet
378 : //__________________________________________________________________________________________________
379 0 : sal_Bool SAL_CALL ManagerImpl::has( const Any & rElement )
380 : throw(::com::sun::star::uno::RuntimeException)
381 : {
382 0 : Reference< XHierarchicalNameAccess > xElem;
383 0 : if (rElement >>= xElem)
384 : {
385 0 : MutexGuard aGuard( _aComponentMutex );
386 0 : return (find( _aProviders.begin(), _aProviders.end(), xElem ) != _aProviders.end());
387 : }
388 0 : return sal_False;
389 : }
390 :
391 : //__________________________________________________________________________________________________
392 259 : void SAL_CALL ManagerImpl::insert( const Any & rElement )
393 : throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::ElementExistException, ::com::sun::star::uno::RuntimeException)
394 : {
395 259 : Reference< XHierarchicalNameAccess > xElem;
396 259 : if (! (rElement >>= xElem) || !xElem.is())
397 : {
398 : throw IllegalArgumentException(
399 : OUString( RTL_CONSTASCII_USTRINGPARAM("no valid type description provider given!") ),
400 0 : (XWeak *)(OWeakObject *)this, 0 );
401 : }
402 :
403 259 : MutexGuard aGuard( _aComponentMutex );
404 259 : if (find( _aProviders.begin(), _aProviders.end(), xElem ) != _aProviders.end())
405 : {
406 : throw ElementExistException(
407 : OUString( RTL_CONSTASCII_USTRINGPARAM("provider already inserted!") ),
408 0 : (XWeak *)(OWeakObject *)this );
409 : }
410 :
411 259 : if (! _aProviders.empty())
412 : {
413 : // check whether all types are compatible, if possible:
414 : Reference<reflection::XTypeDescriptionEnumerationAccess> xTDEnumAccess(
415 0 : xElem, UNO_QUERY );
416 : OSL_ENSURE( xTDEnumAccess.is(),
417 : "### providers ought to implement "
418 : "reflection::XTypeDescriptionEnumerationAccess!" );
419 0 : if (xTDEnumAccess.is())
420 : {
421 : try
422 : {
423 : TypeClass ar [] = {
424 : TypeClass_ENUM, TypeClass_TYPEDEF, TypeClass_SEQUENCE,
425 : TypeClass_STRUCT, TypeClass_EXCEPTION,
426 : /* TypeClass_UNION, TypeClass_ARRAY not supported */
427 : TypeClass_INTERFACE,
428 : TypeClass_SERVICE,
429 : TypeClass_INTERFACE_METHOD, TypeClass_INTERFACE_ATTRIBUTE,
430 : TypeClass_PROPERTY, TypeClass_CONSTANT, TypeClass_CONSTANTS,
431 : TypeClass_SINGLETON
432 0 : };
433 : Reference<reflection::XTypeDescriptionEnumeration> xTDEnum(
434 0 : xTDEnumAccess->createTypeDescriptionEnumeration(
435 : OUString() /* all modules */,
436 : Sequence<TypeClass>( ar, ARLEN(ar) ),
437 0 : reflection::TypeDescriptionSearchDepth_INFINITE ) );
438 :
439 0 : while (xTDEnum->hasMoreElements())
440 : {
441 0 : Reference<reflection::XTypeDescription> xNewTD;
442 : try
443 : {
444 0 : xNewTD = xTDEnum->nextTypeDescription();
445 : }
446 0 : catch (const container::NoSuchElementException & exc)
447 : {
448 : throw lang::IllegalArgumentException(
449 : OUSTR("NoSuchElementException occurred: ") +
450 0 : exc.Message, static_cast<OWeakObject *>(this),
451 0 : -1 /* unknown */ );
452 : }
453 :
454 : try
455 : {
456 0 : OUString newName( xNewTD->getName() );
457 : Reference<reflection::XTypeDescription> xExistingTD(
458 0 : getByHierarchicalName( newName ), UNO_QUERY );
459 : OSL_ASSERT( xExistingTD.is() );
460 : // existing, check whether compatible:
461 0 : if (xExistingTD.is())
462 : {
463 : try
464 : {
465 0 : check( xNewTD, xExistingTD );
466 : }
467 0 : catch (const IncompatibleTypeException & exc)
468 : {
469 : throw lang::IllegalArgumentException(
470 : OUSTR("Rejecting types due to "
471 0 : "incompatibility! ") + exc.m_cause,
472 0 : static_cast<OWeakObject *>(this), 0 );
473 : }
474 0 : }
475 : }
476 0 : catch (container::NoSuchElementException &)
477 : {
478 : // type not in: ok
479 : }
480 0 : }
481 : }
482 0 : catch (const reflection::NoSuchTypeNameException & exc)
483 : {
484 : throw lang::IllegalArgumentException(
485 0 : OUSTR("NoSuchTypeNameException occurred: ") + exc.Message,
486 0 : static_cast<OWeakObject *>(this), -1 /* unknown */ );
487 : }
488 0 : catch (const reflection::InvalidTypeNameException & exc)
489 : {
490 : throw lang::IllegalArgumentException(
491 0 : OUSTR("InvalidTypeNameException occurred: ") + exc.Message,
492 0 : static_cast<OWeakObject *>(this), -1 /* unknown */ );
493 : }
494 0 : }
495 : }
496 :
497 259 : _aProviders.push_back( xElem );
498 259 : Reference< XComponent > xComp( xElem, UNO_QUERY );
499 259 : if (xComp.is())
500 259 : xComp->addEventListener( &_aEventListener );
501 259 : }
502 : //__________________________________________________________________________________________________
503 31 : void SAL_CALL ManagerImpl::remove( const Any & rElement )
504 : throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException)
505 : {
506 31 : if (!rBHelper.bDisposed && !rBHelper.bInDispose)
507 : {
508 0 : Reference< XHierarchicalNameAccess > xElem;
509 0 : if (! (rElement >>= xElem))
510 : {
511 : throw IllegalArgumentException(
512 : OUString( RTL_CONSTASCII_USTRINGPARAM("no type description provider given!") ),
513 0 : (XWeak *)(OWeakObject *)this, 0 );
514 : }
515 :
516 0 : MutexGuard aGuard( _aComponentMutex );
517 0 : ProviderVector::iterator iFind( find( _aProviders.begin(), _aProviders.end(), xElem ) );
518 0 : if (iFind == _aProviders.end())
519 : {
520 : throw NoSuchElementException(
521 : OUString( RTL_CONSTASCII_USTRINGPARAM("provider not found!") ),
522 0 : (XWeak *)(OWeakObject *)this );
523 : }
524 0 : _aProviders.erase( iFind );
525 : }
526 :
527 31 : Reference< XComponent > xComp;
528 31 : if (rElement >>= xComp)
529 31 : xComp->removeEventListener( &_aEventListener );
530 31 : }
531 :
532 : // XTypeDescriptionEnumerationAccess
533 : //__________________________________________________________________________________________________
534 : // virtual
535 : Reference< XTypeDescriptionEnumeration > SAL_CALL
536 0 : ManagerImpl::createTypeDescriptionEnumeration(
537 : const OUString & moduleName,
538 : const Sequence< TypeClass > & types,
539 : TypeDescriptionSearchDepth depth )
540 : throw ( NoSuchTypeNameException,
541 : InvalidTypeNameException,
542 : RuntimeException )
543 : {
544 0 : MutexGuard aGuard( _aComponentMutex );
545 :
546 0 : TDEnumerationAccessStack aStack;
547 0 : ProviderVector::const_iterator it = _aProviders.begin();
548 0 : const ProviderVector::const_iterator end = _aProviders.end();
549 0 : while ( it != end )
550 : {
551 : Reference< XTypeDescriptionEnumerationAccess >xEnumAccess(
552 0 : (*it), UNO_QUERY );
553 : OSL_ENSURE( xEnumAccess.is(),
554 : "### no XTypeDescriptionEnumerationAccess!" );
555 0 : if ( xEnumAccess.is() )
556 0 : aStack.push( xEnumAccess );
557 :
558 0 : ++it;
559 0 : }
560 :
561 : return Reference< XTypeDescriptionEnumeration >(
562 : new TypeDescriptionEnumerationImpl( moduleName,
563 : types,
564 : depth,
565 0 : aStack ) );
566 : }
567 :
568 :
569 : //##################################################################################################
570 : //##################################################################################################
571 : //##################################################################################################
572 :
573 :
574 : //==================================================================================================
575 414 : class SimpleTypeDescriptionImpl
576 : : public WeakImplHelper1< XTypeDescription >
577 : {
578 : TypeClass _eTC;
579 : OUString _aName;
580 :
581 : public:
582 224 : SimpleTypeDescriptionImpl( TypeClass eTC, const OUString & rName )
583 : : _eTC( eTC )
584 224 : , _aName( rName )
585 224 : {}
586 :
587 : // XTypeDescription
588 : virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
589 : virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
590 : };
591 :
592 : // XTypeDescription
593 : //__________________________________________________________________________________________________
594 8323 : TypeClass SimpleTypeDescriptionImpl::getTypeClass()
595 : throw(::com::sun::star::uno::RuntimeException)
596 : {
597 8323 : return _eTC;
598 : }
599 : //__________________________________________________________________________________________________
600 13112 : OUString SimpleTypeDescriptionImpl::getName()
601 : throw(::com::sun::star::uno::RuntimeException)
602 : {
603 13112 : return _aName;
604 : }
605 :
606 : //==================================================================================================
607 320 : class SequenceTypeDescriptionImpl
608 : : public WeakImplHelper1< XIndirectTypeDescription >
609 : {
610 : Reference< XTypeDescription > _xElementTD;
611 :
612 : public:
613 162 : SequenceTypeDescriptionImpl( const Reference< XTypeDescription > & xElementTD )
614 162 : : _xElementTD( xElementTD )
615 162 : {}
616 :
617 : // XTypeDescription
618 : virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
619 : virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
620 :
621 : // XIndirectTypeDescription
622 : virtual Reference< XTypeDescription > SAL_CALL getReferencedType() throw(::com::sun::star::uno::RuntimeException);
623 : };
624 :
625 : // XTypeDescription
626 : //__________________________________________________________________________________________________
627 585 : TypeClass SequenceTypeDescriptionImpl::getTypeClass()
628 : throw(::com::sun::star::uno::RuntimeException)
629 : {
630 585 : return TypeClass_SEQUENCE;
631 : }
632 : //__________________________________________________________________________________________________
633 563 : OUString SequenceTypeDescriptionImpl::getName()
634 : throw(::com::sun::star::uno::RuntimeException)
635 : {
636 563 : return (OUString( RTL_CONSTASCII_USTRINGPARAM("[]") ) + _xElementTD->getName());
637 : }
638 :
639 : // XIndirectTypeDescription
640 : //__________________________________________________________________________________________________
641 32 : Reference< XTypeDescription > SequenceTypeDescriptionImpl::getReferencedType()
642 : throw(::com::sun::star::uno::RuntimeException)
643 : {
644 32 : return _xElementTD;
645 : }
646 :
647 : //==================================================================================================
648 : class ArrayTypeDescriptionImpl
649 : : public WeakImplHelper1< XArrayTypeDescription >
650 : {
651 : Reference< XTypeDescription > _xElementTD;
652 : Mutex _aDimensionMutex;
653 : sal_Int32 _nDimensions;
654 : Sequence< sal_Int32 > _seqDimensions;
655 : OUString _sDimensions;
656 :
657 : void initDimensions(const OUString& rSDimensions);
658 : public:
659 0 : ArrayTypeDescriptionImpl( const Reference< XTypeDescription > & xElementTD,
660 : sal_Int32 nDimensions, const OUString& rSDimensions )
661 : : _xElementTD( xElementTD )
662 : , _nDimensions( nDimensions )
663 : , _seqDimensions( Sequence< sal_Int32 >(nDimensions) )
664 0 : , _sDimensions( rSDimensions )
665 : {
666 0 : initDimensions( rSDimensions );
667 0 : }
668 0 : virtual ~ArrayTypeDescriptionImpl() {}
669 :
670 : // XTypeDescription
671 : virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
672 : virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
673 :
674 : // XArrayTypeDescription
675 : virtual Reference< XTypeDescription > SAL_CALL getType() throw(::com::sun::star::uno::RuntimeException);
676 : virtual sal_Int32 SAL_CALL getNumberOfDimensions() throw(::com::sun::star::uno::RuntimeException);
677 : virtual Sequence< sal_Int32 > SAL_CALL getDimensions() throw(::com::sun::star::uno::RuntimeException);
678 : };
679 : //__________________________________________________________________________________________________
680 0 : static sal_Int32 unicodeToInteger( sal_Int8 base, const sal_Unicode *s )
681 : {
682 0 : sal_Int32 r = 0;
683 0 : sal_Int32 negative = 0;
684 :
685 0 : if (*s == '-')
686 : {
687 0 : negative = 1;
688 0 : s++;
689 : }
690 0 : if (base == 8 && *s == '0')
691 0 : s++;
692 0 : else if (base == 16 && *s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))
693 0 : s += 2;
694 :
695 0 : for (; *s; s++)
696 : {
697 0 : if (*s <= '9' && *s >= '0')
698 0 : r = (r * base) + (*s - '0');
699 0 : else if (base > 10 && *s <= 'f' && *s >= 'a')
700 0 : r = (r * base) + (*s - 'a' + 10);
701 0 : else if (base > 10 && *s <= 'F' && *s >= 'A')
702 0 : r = (r * base) + (*s - 'A' + 10);
703 : else
704 0 : break;
705 : }
706 0 : if (negative) r *= -1;
707 0 : return r;
708 : }
709 : //__________________________________________________________________________________________________
710 0 : void ArrayTypeDescriptionImpl::initDimensions(const OUString& rSDimensions)
711 : {
712 0 : MutexGuard aGuard( _aDimensionMutex );
713 :
714 0 : sal_Int32 * pDimensions = _seqDimensions.getArray();
715 0 : OUString tmp(rSDimensions);
716 0 : sal_Unicode* p = (sal_Unicode*)tmp.getStr()+1;
717 0 : sal_Unicode* pOffset = p;
718 0 : sal_Int32 len = tmp.getLength() - 1 ;
719 0 : sal_Int32 i = 0;
720 :
721 0 : while ( len > 0)
722 : {
723 0 : pOffset++;
724 0 : if (*pOffset == ']')
725 : {
726 0 : *pOffset = '\0';
727 0 : pOffset += 2;
728 0 : len -= 3;
729 0 : pDimensions[i++] = unicodeToInteger(10, p);
730 0 : p = pOffset;
731 : } else
732 0 : len--;
733 0 : }
734 0 : }
735 :
736 : // XTypeDescription
737 : //__________________________________________________________________________________________________
738 0 : TypeClass ArrayTypeDescriptionImpl::getTypeClass()
739 : throw(::com::sun::star::uno::RuntimeException)
740 : {
741 0 : return TypeClass_ARRAY;
742 : }
743 : //__________________________________________________________________________________________________
744 0 : OUString ArrayTypeDescriptionImpl::getName()
745 : throw(::com::sun::star::uno::RuntimeException)
746 : {
747 0 : return (_xElementTD->getName() + _sDimensions);
748 : }
749 :
750 : // XArrayTypeDescription
751 : //__________________________________________________________________________________________________
752 0 : Reference< XTypeDescription > ArrayTypeDescriptionImpl::getType()
753 : throw(::com::sun::star::uno::RuntimeException)
754 : {
755 0 : return _xElementTD;
756 : }
757 :
758 : //__________________________________________________________________________________________________
759 0 : sal_Int32 ArrayTypeDescriptionImpl::getNumberOfDimensions()
760 : throw(::com::sun::star::uno::RuntimeException)
761 : {
762 0 : return _nDimensions;
763 : }
764 :
765 : //__________________________________________________________________________________________________
766 0 : Sequence< sal_Int32 > ArrayTypeDescriptionImpl::getDimensions()
767 : throw(::com::sun::star::uno::RuntimeException)
768 : {
769 0 : return _seqDimensions;
770 : }
771 :
772 : //##################################################################################################
773 : //##################################################################################################
774 : //##################################################################################################
775 :
776 :
777 : //__________________________________________________________________________________________________
778 225 : inline Any ManagerImpl::getSimpleType( const OUString & rName )
779 : {
780 225 : Any aRet;
781 :
782 225 : if ( rName == "string" )
783 32 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_STRING, rName ) );
784 193 : else if ( rName == "long" )
785 21 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_LONG, rName ) );
786 172 : else if ( rName == "unsigned long" )
787 10 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_UNSIGNED_LONG, rName ) );
788 162 : else if ( rName == "boolean" )
789 20 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_BOOLEAN, rName ) );
790 142 : else if ( rName == "char" )
791 8 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_CHAR, rName ) );
792 134 : else if ( rName == "byte" )
793 9 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_BYTE, rName ) );
794 125 : else if ( rName == "short" )
795 18 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_SHORT, rName ) );
796 107 : else if ( rName == "unsigned short" )
797 19 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_UNSIGNED_SHORT, rName ) );
798 88 : else if ( rName == "hyper" )
799 9 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_HYPER, rName ) );
800 79 : else if ( rName == "unsigned hyper" )
801 1 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_UNSIGNED_HYPER, rName ) );
802 78 : else if ( rName == "float" )
803 8 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_FLOAT, rName ) );
804 70 : else if ( rName == "double" )
805 8 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_DOUBLE, rName ) );
806 62 : else if ( rName == "any" )
807 32 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_ANY, rName ) );
808 30 : else if ( rName == "void" )
809 9 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_VOID, rName ) );
810 21 : else if ( rName == "type" )
811 20 : aRet <<= Reference< XTypeDescription >( new SimpleTypeDescriptionImpl( TypeClass_TYPE, rName ) );
812 :
813 225 : return aRet;
814 : }
815 :
816 : namespace {
817 :
818 64 : Reference< XTypeDescription > resolveTypedefs(
819 : Reference< XTypeDescription > const & type)
820 : {
821 64 : Reference< XTypeDescription > resolved(type);
822 128 : while (resolved->getTypeClass() == TypeClass_TYPEDEF) {
823 : resolved = Reference< XIndirectTypeDescription >(
824 0 : type, UNO_QUERY_THROW)->getReferencedType();
825 : }
826 64 : return resolved;
827 : }
828 :
829 64 : bool isNonVoidNonExceptionType(Reference< XTypeDescription > const & type) {
830 64 : switch (type->getTypeClass()) {
831 : case TypeClass_BOOLEAN:
832 : case TypeClass_BYTE:
833 : case TypeClass_SHORT:
834 : case TypeClass_UNSIGNED_SHORT:
835 : case TypeClass_LONG:
836 : case TypeClass_UNSIGNED_LONG:
837 : case TypeClass_HYPER:
838 : case TypeClass_UNSIGNED_HYPER:
839 : case TypeClass_FLOAT:
840 : case TypeClass_DOUBLE:
841 : case TypeClass_CHAR:
842 : case TypeClass_STRING:
843 : case TypeClass_TYPE:
844 : case TypeClass_ANY:
845 : case TypeClass_SEQUENCE:
846 : case TypeClass_ENUM:
847 : case TypeClass_STRUCT:
848 : case TypeClass_INTERFACE:
849 64 : return true;
850 :
851 : default:
852 0 : return false;
853 : }
854 : }
855 :
856 80 : class InstantiatedStruct: public WeakImplHelper1< XStructTypeDescription > {
857 : public:
858 : InstantiatedStruct(
859 : Reference< XStructTypeDescription > const & structType,
860 : std::vector< Reference< XTypeDescription > > const & arguments);
861 :
862 88 : virtual TypeClass SAL_CALL getTypeClass() throw (RuntimeException)
863 88 : { return TypeClass_STRUCT; }
864 :
865 : virtual OUString SAL_CALL getName() throw (RuntimeException);
866 :
867 50 : virtual Reference< XTypeDescription > SAL_CALL getBaseType()
868 : throw (RuntimeException)
869 50 : { return m_struct->getBaseType(); }
870 :
871 : virtual Sequence< Reference< XTypeDescription > > SAL_CALL getMemberTypes()
872 : throw (RuntimeException);
873 :
874 50 : virtual Sequence< OUString > SAL_CALL getMemberNames()
875 : throw (RuntimeException)
876 50 : { return m_struct->getMemberNames(); }
877 :
878 50 : virtual Sequence< OUString > SAL_CALL getTypeParameters()
879 : throw (RuntimeException)
880 50 : { return Sequence< OUString >(); }
881 :
882 : virtual Sequence< Reference< XTypeDescription > > SAL_CALL
883 0 : getTypeArguments() throw (RuntimeException)
884 0 : { return m_arguments; }
885 :
886 : private:
887 : Reference< XStructTypeDescription > m_struct;
888 : Sequence< Reference< XTypeDescription > > m_arguments;
889 : };
890 :
891 50 : InstantiatedStruct::InstantiatedStruct(
892 : Reference< XStructTypeDescription > const & structType,
893 : std::vector< Reference< XTypeDescription > > const & arguments):
894 : m_struct(structType),
895 50 : m_arguments(static_cast< sal_Int32 >(arguments.size()))
896 : {
897 228 : for (std::vector< Reference< XTypeDescription > >::size_type i = 0;
898 114 : i < arguments.size(); ++i)
899 : {
900 64 : m_arguments[static_cast< sal_Int32 >(i)] = arguments[i];
901 : }
902 50 : }
903 :
904 90 : OUString InstantiatedStruct::getName() throw (RuntimeException) {
905 90 : OUStringBuffer buf(m_struct->getName());
906 90 : buf.append(static_cast< sal_Unicode >('<'));
907 210 : for (sal_Int32 i = 0; i < m_arguments.getLength(); ++i) {
908 120 : if (i != 0) {
909 30 : buf.append(static_cast< sal_Unicode >(','));
910 : }
911 120 : buf.append(m_arguments[i]->getName());
912 : }
913 90 : buf.append(static_cast< sal_Unicode >('>'));
914 90 : return buf.makeStringAndClear();
915 : }
916 :
917 50 : Sequence< Reference< XTypeDescription > > InstantiatedStruct::getMemberTypes()
918 : throw (RuntimeException)
919 : {
920 50 : Sequence< Reference< XTypeDescription > > types(m_struct->getMemberTypes());
921 114 : for (sal_Int32 i = 0; i < types.getLength(); ++i) {
922 64 : if (types[i]->getTypeClass() == TypeClass_UNKNOWN) {
923 64 : Sequence< OUString > parameters(m_struct->getTypeParameters());
924 : OSL_ASSERT(parameters.getLength() == m_arguments.getLength());
925 78 : for (sal_Int32 j = 0; j < parameters.getLength(); ++j) {
926 78 : if (parameters[j] == types[i]->getName()) {
927 64 : types[i] = m_arguments[j];
928 64 : break;
929 : }
930 64 : }
931 : }
932 : }
933 50 : return types;
934 : }
935 :
936 : }
937 :
938 50 : Reference< XTypeDescription > ManagerImpl::getInstantiatedStruct(
939 : OUString const & name)
940 : {
941 50 : sal_Int32 i = name.indexOf('<');
942 : OSL_ASSERT(i >= 0);
943 : Reference< XStructTypeDescription > structType(
944 50 : getByHierarchicalName(name.copy(0, i)), UNO_QUERY);
945 50 : std::vector< Reference< XTypeDescription > > args;
946 50 : bool good = structType.is();
947 50 : if (good) {
948 128 : do {
949 64 : ++i; // skip '<' or ','
950 64 : sal_Int32 j = i;
951 1241 : for (sal_Int32 level = 0; j != name.getLength(); ++j) {
952 1241 : sal_Unicode c = name[j];
953 1241 : if (c == ',') {
954 22 : if (level == 0) {
955 14 : break;
956 : }
957 1219 : } else if (c == '<') {
958 13 : ++level;
959 1206 : } else if (c == '>') {
960 63 : if (level == 0) {
961 50 : break;
962 : }
963 13 : --level;
964 : }
965 : }
966 64 : if (j != name.getLength()) {
967 : Reference< XTypeDescription > type(
968 64 : getByHierarchicalName(name.copy(i, j - i)), UNO_QUERY);
969 64 : if (isNonVoidNonExceptionType(resolveTypedefs(type))) {
970 64 : args.push_back(type);
971 : } else {
972 0 : good = false;
973 : break;
974 64 : }
975 : }
976 64 : i = j;
977 128 : } while (i != name.getLength() && name[i] != '>');
978 50 : good = good && i == name.getLength() - 1
979 100 : && name[i] == '>' && !args.empty();
980 : }
981 : // args.size() cannot exceed SAL_MAX_INT32, as each argument consumes at
982 : // least one position within an rtl::OUString (which is no longer than
983 : // SAL_MAX_INT32):
984 200 : if (!good
985 50 : || (args.size()
986 : != sal::static_int_cast< sal_uInt32 >(
987 150 : structType->getTypeParameters().getLength())))
988 : {
989 0 : throw NoSuchElementException(name, static_cast< OWeakObject * >(this));
990 : }
991 50 : return new InstantiatedStruct(structType, args);
992 : }
993 :
994 : // XHierarchicalNameAccess
995 : //__________________________________________________________________________________________________
996 17522 : Any ManagerImpl::getByHierarchicalName( const OUString & rName )
997 : throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException)
998 : {
999 17522 : Any aRet;
1000 17522 : if (_bCaching)
1001 17522 : aRet = _aElements.getValue( rName );
1002 17522 : if (!rName.isEmpty() && !aRet.hasValue())
1003 : {
1004 : sal_Int32 nIndex;
1005 6426 : if (rName[0] == '[') // test for sequence
1006 : {
1007 : Reference< XTypeDescription > xElemType(
1008 162 : getByHierarchicalName( rName.copy( 2 ) ),
1009 162 : UNO_QUERY_THROW );
1010 : aRet <<= Reference< XTypeDescription >(
1011 162 : new SequenceTypeDescriptionImpl( xElemType ) );
1012 : }
1013 6264 : else if (rName[rName.getLength()-1] == ']') // test for array
1014 : {
1015 0 : sal_Int32 nIndex2 = 0, nTokens = 0;
1016 0 : do { rName.getToken( 0, '[', nIndex2 ); nTokens++; } while( nIndex2 != -1 );
1017 0 : sal_Int32 nDims = nTokens - 1;
1018 0 : sal_Int32 dimOffset = rName.indexOf('[');
1019 : Reference< XTypeDescription > xElemType(
1020 0 : getByHierarchicalName( rName.copy( 0, dimOffset ) ),
1021 0 : UNO_QUERY_THROW );
1022 : aRet <<= Reference< XTypeDescription >(
1023 : new ArrayTypeDescriptionImpl(
1024 0 : xElemType, nDims, rName.copy(dimOffset) ) );
1025 : }
1026 : // test for interface member names:
1027 6264 : else if ((nIndex = rName.indexOf( ':' )) >= 0)
1028 : {
1029 : Reference< XInterfaceTypeDescription > xIfaceTD(
1030 1842 : getByHierarchicalName( rName.copy( 0, nIndex ) ),
1031 1842 : UNO_QUERY_THROW );
1032 : const Sequence< Reference< XInterfaceMemberTypeDescription > > &
1033 1842 : rMembers = xIfaceTD->getMembers();
1034 : const Reference< XInterfaceMemberTypeDescription > * pMembers =
1035 1842 : rMembers.getConstArray();
1036 :
1037 11030 : for ( sal_Int32 nPos = rMembers.getLength(); nPos--; )
1038 : {
1039 9188 : if (rName == pMembers[nPos]->getName())
1040 : {
1041 : aRet <<= Reference< XTypeDescription >(
1042 1842 : pMembers[nPos], UNO_QUERY_THROW );
1043 1842 : break;
1044 : }
1045 : }
1046 1842 : if (! aRet.hasValue())
1047 : {
1048 : // member not found:
1049 : throw NoSuchElementException(
1050 0 : rName, static_cast< OWeakObject * >(this) );
1051 1842 : }
1052 : }
1053 : // test for instantiated polymorphic struct types:
1054 4422 : else if (rName.indexOf('<') >= 0)
1055 : {
1056 50 : aRet <<= getInstantiatedStruct(rName);
1057 : }
1058 4372 : else if (rName.indexOf( '.' ) < 0) // test for simple/ build in types
1059 : {
1060 225 : aRet = getSimpleType( rName );
1061 : }
1062 :
1063 6426 : if (! aRet.hasValue())
1064 : {
1065 : // last, try callback chain
1066 12447 : for ( ProviderVector::const_iterator iPos( _aProviders.begin() );
1067 8298 : iPos != _aProviders.end(); ++iPos )
1068 : {
1069 : try
1070 : {
1071 12442 : if ((aRet = (*iPos)->getByHierarchicalName(
1072 8295 : rName )).hasValue())
1073 : {
1074 4147 : break;
1075 : }
1076 : }
1077 1 : catch (NoSuchElementException &)
1078 : {
1079 : }
1080 : }
1081 : }
1082 :
1083 : // update cache
1084 6426 : if (_bCaching && aRet.hasValue())
1085 6425 : _aElements.setValue( rName, aRet );
1086 : }
1087 :
1088 17522 : if (! aRet.hasValue())
1089 : {
1090 : throw NoSuchElementException(
1091 1 : rName, static_cast< OWeakObject * >(this) );
1092 : }
1093 17522 : return aRet;
1094 : }
1095 : //__________________________________________________________________________________________________
1096 1 : sal_Bool ManagerImpl::hasByHierarchicalName( const OUString & rName )
1097 : throw(::com::sun::star::uno::RuntimeException)
1098 : {
1099 : try
1100 : {
1101 1 : return getByHierarchicalName( rName ).hasValue();
1102 : }
1103 1 : catch (NoSuchElementException &)
1104 : {
1105 : }
1106 1 : return sal_False;
1107 : }
1108 : }
1109 :
1110 : namespace stoc_bootstrap
1111 : {
1112 : //==================================================================================================
1113 259 : Reference< XInterface > SAL_CALL ManagerImpl_create(
1114 : Reference< XComponentContext > const & xContext )
1115 : SAL_THROW( (::com::sun::star::uno::Exception) )
1116 : {
1117 259 : sal_Int32 nCacheSize = CACHE_SIZE;
1118 259 : if (xContext.is()) {
1119 259 : xContext->getValueByName(
1120 : OUString(
1121 : RTL_CONSTASCII_USTRINGPARAM(
1122 259 : "/implementations/" IMPLNAME "/CacheSize"))) >>=
1123 259 : nCacheSize;
1124 : }
1125 :
1126 259 : return Reference< XInterface >( *new stoc_tdmgr::ManagerImpl( xContext, nCacheSize ) );
1127 : }
1128 :
1129 : }
1130 :
1131 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|