update dev300-m58
[ooovba.git] / extensions / source / abpilot / datasourcehandling.cxx
blobe1f3065dca864e2cc176e48df94b2c879bb676eb
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: datasourcehandling.cxx,v $
10 * $Revision: 1.15 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_extensions.hxx"
33 #include "datasourcehandling.hxx"
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
36 #include <com/sun/star/container/XNameAccess.hpp>
37 #include <com/sun/star/sdb/SQLContext.hpp>
38 #include <com/sun/star/task/XInteractionHandler.hpp>
39 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/frame/XStorable.hpp>
41 #include <com/sun/star/uno/XNamingService.hpp>
42 #include <com/sun/star/lang/XComponent.hpp>
43 #include <com/sun/star/sdbc/XConnection.hpp>
44 #include <com/sun/star/sdb/XCompletedConnection.hpp>
45 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
46 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
47 #include <tools/debug.hxx>
48 #include <comphelper/interaction.hxx>
49 #include <vcl/stdtext.hxx>
50 #ifndef EXTENSIONS_ABPRESID_HRC
51 #include "abpresid.hrc"
52 #endif
53 #include "componentmodule.hxx"
54 #include "abptypes.hxx"
55 #include <unotools/confignode.hxx>
56 #include <unotools/sharedunocomponent.hxx>
58 //.........................................................................
59 namespace abp
61 //.........................................................................
63 using namespace ::utl;
64 using namespace ::comphelper;
65 using namespace ::com::sun::star::uno;
66 using namespace ::com::sun::star::lang;
67 using namespace ::com::sun::star::sdb;
68 using namespace ::com::sun::star::sdbc;
69 using namespace ::com::sun::star::task;
70 using namespace ::com::sun::star::beans;
71 using namespace ::com::sun::star::sdbcx;
72 using namespace ::com::sun::star::container;
73 using namespace ::com::sun::star::frame;
75 //=====================================================================
76 struct PackageAccessControl { };
78 //=====================================================================
79 //--------------------------------------------------------------------
80 static const ::rtl::OUString& getDbRegisteredNamesNodeName()
82 static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii("org.openoffice.Office.DataAccess/RegisteredNames");
83 return s_sNodeName;
86 //--------------------------------------------------------------------
87 static const ::rtl::OUString& getDbNameNodeName()
89 static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii("Name");
90 return s_sNodeName;
93 //--------------------------------------------------------------------
94 static const ::rtl::OUString& getDbLocationNodeName()
96 static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii("Location");
97 return s_sNodeName;
99 //---------------------------------------------------------------------
100 static Reference< XNameAccess > lcl_getDataSourceContext( const Reference< XMultiServiceFactory >& _rxORB ) SAL_THROW (( Exception ))
102 Reference< XNameAccess > xContext( _rxORB->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.sdb.DatabaseContext" ) ), UNO_QUERY );
103 DBG_ASSERT(xContext.is(), "lcl_getDataSourceContext: could not access the data source context!");
104 return xContext;
107 //---------------------------------------------------------------------
108 /// creates a new data source and inserts it into the context
109 static void lcl_implCreateAndInsert(
110 const Reference< XMultiServiceFactory >& _rxORB, const ::rtl::OUString& _rName,
111 Reference< XPropertySet >& /* [out] */ _rxNewDataSource ) SAL_THROW (( ::com::sun::star::uno::Exception ))
113 //.............................................................
114 // get the data source context
115 Reference< XNameAccess > xContext = lcl_getDataSourceContext( _rxORB );
117 DBG_ASSERT( !xContext->hasByName( _rName ), "lcl_implCreateAndInsert: name already used!" );
118 (void)_rName;
120 //.............................................................
121 // create a new data source
122 Reference< XSingleServiceFactory > xFactory( xContext, UNO_QUERY );
123 Reference< XPropertySet > xNewDataSource;
124 if (xFactory.is())
125 xNewDataSource = Reference< XPropertySet >( xFactory->createInstance(), UNO_QUERY );
126 DBG_ASSERT( xNewDataSource.is(), "lcl_implCreateAndInsert: could not create a new data source!" );
128 //.............................................................
129 // insert the data source into the context
130 Reference< XNamingService > xDynamicContext( xContext, UNO_QUERY );
131 DBG_ASSERT( xDynamicContext.is(), "lcl_implCreateAndInsert: missing an interface on the context (XNamingService)!" );
132 if (xDynamicContext.is())
134 // xDynamicContext->registerObject( _rName, xNewDataSource );
135 _rxNewDataSource = xNewDataSource;
139 //---------------------------------------------------------------------
140 /// creates and inserts a data source, and sets it's URL property to the string given
141 static ODataSource lcl_implCreateAndSetURL(
142 const Reference< XMultiServiceFactory >& _rxORB, const ::rtl::OUString& _rName,
143 const sal_Char* _pInitialAsciiURL ) SAL_THROW (( ))
145 ODataSource aReturn( _rxORB );
148 // create the new data source
149 Reference< XPropertySet > xNewDataSource;
150 lcl_implCreateAndInsert( _rxORB, _rName, xNewDataSource );
152 //.............................................................
153 // set the URL property
154 if (xNewDataSource.is())
156 xNewDataSource->setPropertyValue(
157 ::rtl::OUString::createFromAscii( "URL" ),
158 makeAny( ::rtl::OUString::createFromAscii( _pInitialAsciiURL ) )
162 aReturn.setDataSource( xNewDataSource, _rName,PackageAccessControl() );
164 catch(const Exception&)
166 DBG_ERROR( "lcl_implCreateAndSetURL: caught an exception while creating the data source!" );
169 return aReturn;
171 //---------------------------------------------------------------------
172 void lcl_registerDataSource(
173 const Reference< XMultiServiceFactory >& _rxORB, const ::rtl::OUString& _sName,
174 const ::rtl::OUString& _sURL ) SAL_THROW (( ::com::sun::star::uno::Exception ))
176 // the config node where all pooling relevant info are stored under
177 OConfigurationTreeRoot aDbRegisteredNamesRoot = OConfigurationTreeRoot::createWithServiceFactory(
178 _rxORB, getDbRegisteredNamesNodeName(), -1, OConfigurationTreeRoot::CM_UPDATABLE);
180 if (!aDbRegisteredNamesRoot.isValid())
181 // already asserted by the OConfigurationTreeRoot
182 return;
184 OSL_ENSURE(_sName.getLength(),"No Name given!");
185 OSL_ENSURE(_sURL.getLength(),"No URL given!");
187 OConfigurationNode aThisDriverSettings;
188 if ( aDbRegisteredNamesRoot.hasByName(_sName) )
189 aThisDriverSettings = aDbRegisteredNamesRoot.openNode(_sName);
190 else
191 aThisDriverSettings = aDbRegisteredNamesRoot.createNode(_sName);
193 // set the values
194 aThisDriverSettings.setNodeValue(getDbNameNodeName(), makeAny(_sName));
195 aThisDriverSettings.setNodeValue(getDbLocationNodeName(), makeAny(_sURL));
197 aDbRegisteredNamesRoot.commit();
200 //=====================================================================
201 //= ODataSourceContextImpl
202 //=====================================================================
203 struct ODataSourceContextImpl
205 Reference< XMultiServiceFactory > xORB;
206 Reference< XNameAccess > xContext; /// the UNO data source context
207 StringBag aDataSourceNames; /// for quicker name checks (without the UNO overhead)
209 ODataSourceContextImpl( const Reference< XMultiServiceFactory >& _rxORB ) : xORB( _rxORB ) { }
210 ODataSourceContextImpl( const ODataSourceContextImpl& _rSource )
211 :xORB ( _rSource.xORB )
212 ,xContext ( _rSource.xContext )
217 //=====================================================================
218 //= ODataSourceContext
219 //=====================================================================
220 //---------------------------------------------------------------------
221 ODataSourceContext::ODataSourceContext(const Reference< XMultiServiceFactory >& _rxORB)
222 :m_pImpl( new ODataSourceContextImpl( _rxORB ) )
226 // create the UNO context
227 m_pImpl->xContext = lcl_getDataSourceContext( _rxORB );
229 if (m_pImpl->xContext.is())
231 // collect the data source names
232 Sequence< ::rtl::OUString > aDSNames = m_pImpl->xContext->getElementNames();
233 const ::rtl::OUString* pDSNames = aDSNames.getConstArray();
234 const ::rtl::OUString* pDSNamesEnd = pDSNames + aDSNames.getLength();
236 for ( ;pDSNames != pDSNamesEnd; ++pDSNames )
237 m_pImpl->aDataSourceNames.insert( *pDSNames );
240 catch( const Exception& )
242 DBG_ERROR( "ODataSourceContext::ODataSourceContext: caught an exception!" );
246 //---------------------------------------------------------------------
247 ::rtl::OUString& ODataSourceContext::disambiguate(::rtl::OUString& _rDataSourceName)
249 ::rtl::OUString sCheck( _rDataSourceName );
250 ConstStringBagIterator aPos = m_pImpl->aDataSourceNames.find( sCheck );
252 sal_Int32 nPostFix = 1;
253 while ( ( m_pImpl->aDataSourceNames.end() != aPos ) && ( nPostFix < 65535 ) )
254 { // there already is a data source with this name
255 sCheck = _rDataSourceName;
256 sCheck += ::rtl::OUString::valueOf( nPostFix++ );
258 aPos = m_pImpl->aDataSourceNames.find( sCheck );
261 _rDataSourceName = sCheck;
262 return _rDataSourceName;
265 //---------------------------------------------------------------------
266 void ODataSourceContext::getDataSourceNames( StringBag& _rNames ) const SAL_THROW (( ))
268 _rNames = m_pImpl->aDataSourceNames;
271 //---------------------------------------------------------------------
272 ODataSource ODataSourceContext::createNewLDAP( const ::rtl::OUString& _rName) SAL_THROW (( ))
274 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:ldap:" );
277 //---------------------------------------------------------------------
278 ODataSource ODataSourceContext::createNewMORK( const ::rtl::OUString& _rName) SAL_THROW (( ))
280 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:mozilla" );
283 //---------------------------------------------------------------------
284 ODataSource ODataSourceContext::createNewThunderbird( const ::rtl::OUString& _rName ) SAL_THROW (( ))
286 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:thunderbird" );
289 //---------------------------------------------------------------------
290 ODataSource ODataSourceContext::createNewEvolutionLdap( const ::rtl::OUString& _rName) SAL_THROW (( ))
292 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:ldap" );
294 //---------------------------------------------------------------------
295 ODataSource ODataSourceContext::createNewEvolutionGroupwise( const ::rtl::OUString& _rName) SAL_THROW (( ))
297 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:groupwise" );
299 //---------------------------------------------------------------------
300 ODataSource ODataSourceContext::createNewEvolution( const ::rtl::OUString& _rName) SAL_THROW (( ))
302 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:local" );
305 //---------------------------------------------------------------------
306 ODataSource ODataSourceContext::createNewKab( const ::rtl::OUString& _rName) SAL_THROW (( ))
308 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:kab" );
311 //---------------------------------------------------------------------
312 ODataSource ODataSourceContext::createNewMacab( const ::rtl::OUString& _rName) SAL_THROW (( ))
314 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:macab" );
317 //---------------------------------------------------------------------
318 ODataSource ODataSourceContext::createNewOutlook( const ::rtl::OUString& _rName) SAL_THROW (( ))
320 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:outlook" );
323 //---------------------------------------------------------------------
324 ODataSource ODataSourceContext::createNewOE( const ::rtl::OUString& _rName) SAL_THROW (( ))
326 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:outlookexp" );
329 //---------------------------------------------------------------------
330 ODataSource ODataSourceContext::createNewDBase( const ::rtl::OUString& _rName) SAL_THROW (( ))
332 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:dbase:" );
335 //=====================================================================
336 //= ODataSourceImpl
337 //=====================================================================
338 struct ODataSourceImpl
340 public:
341 Reference< XMultiServiceFactory > xORB; /// the service factory
342 Reference< XPropertySet > xDataSource; /// the UNO data source
343 ::utl::SharedUNOComponent< XConnection >
344 xConnection;
345 StringBag aTables; // the cached table names
346 ::rtl::OUString sName;
347 sal_Bool bTablesUpToDate; // table name cache up-to-date?
349 ODataSourceImpl( const Reference< XMultiServiceFactory >& _rxORB )
350 :xORB( _rxORB )
351 ,bTablesUpToDate( sal_False )
355 ODataSourceImpl( const ODataSourceImpl& _rSource );
358 //---------------------------------------------------------------------
359 ODataSourceImpl::ODataSourceImpl( const ODataSourceImpl& _rSource )
360 :xORB( _rSource.xORB )
361 ,xDataSource( _rSource.xDataSource )
362 ,xConnection( _rSource.xConnection )
363 ,aTables( _rSource.aTables )
364 ,sName( _rSource.sName )
365 ,bTablesUpToDate( _rSource.bTablesUpToDate )
369 //=====================================================================
370 //= ODataSource
371 //=====================================================================
372 //---------------------------------------------------------------------
373 ODataSource::ODataSource( const Reference< XMultiServiceFactory >& _rxORB, const ::rtl::OUString& _rName )
374 :m_pImpl(new ODataSourceImpl(_rxORB))
378 // get the data source context
379 Reference< XNameAccess > xContext = lcl_getDataSourceContext( m_pImpl->xORB );
381 // retrieve the UNO data source
382 if (xContext.is())
383 xContext->getByName( _rName ) >>= m_pImpl->xDataSource;
385 catch(const Exception&)
387 DBG_ERROR("ODataSource::ODataSource: could not access the requested data source (caught an exception)!");
391 //---------------------------------------------------------------------
392 ODataSource::ODataSource( const ODataSource& _rSource )
393 :m_pImpl( NULL )
395 *this = _rSource;
398 //---------------------------------------------------------------------
399 ODataSource& ODataSource::operator=( const ODataSource& _rSource )
401 delete m_pImpl;
402 m_pImpl = new ODataSourceImpl( *_rSource.m_pImpl );
404 return *this;
407 //---------------------------------------------------------------------
408 ODataSource::ODataSource( const Reference< XMultiServiceFactory >& _rxORB )
409 :m_pImpl(new ODataSourceImpl(_rxORB))
413 //---------------------------------------------------------------------
414 ODataSource::~ODataSource( )
416 delete m_pImpl;
419 //---------------------------------------------------------------------
420 void ODataSource::store() SAL_THROW (( ))
422 if (!isValid())
423 // nothing to do
424 return;
427 Reference< XDocumentDataSource > xDocAccess( m_pImpl->xDataSource, UNO_QUERY );
428 Reference< XStorable > xStorable;
429 if ( xDocAccess.is() )
430 xStorable = xStorable.query( xDocAccess->getDatabaseDocument() );
431 OSL_ENSURE( xStorable.is(),"DataSource is no XStorable!" );
432 if ( xStorable.is() )
433 xStorable->storeAsURL(m_pImpl->sName,Sequence<PropertyValue>());
435 catch(const Exception&)
437 DBG_ERROR( "ODataSource::registerDataSource: caught an exception while creating the data source!" );
440 //---------------------------------------------------------------------
441 void ODataSource::registerDataSource( const ::rtl::OUString& _sRegisteredDataSourceName) SAL_THROW (( ))
443 if (!isValid())
444 // nothing to do
445 return;
449 // invalidate ourself
450 lcl_registerDataSource(m_pImpl->xORB,_sRegisteredDataSourceName,m_pImpl->sName);
452 catch(const Exception&)
454 DBG_ERROR( "ODataSource::registerDataSource: caught an exception while creating the data source!" );
458 //---------------------------------------------------------------------
459 void ODataSource::setDataSource( const Reference< XPropertySet >& _rxDS,const ::rtl::OUString& _sName, PackageAccessControl )
461 if (m_pImpl->xDataSource.get() == _rxDS.get())
462 // nothing to do
463 return;
465 if ( isConnected() )
466 disconnect();
468 m_pImpl->sName = _sName;
469 m_pImpl->xDataSource = _rxDS;
472 //---------------------------------------------------------------------
473 void ODataSource::remove() SAL_THROW (( ))
475 if (!isValid())
476 // nothing to do
477 return;
481 // invalidate ourself
482 m_pImpl->xDataSource.clear();
484 catch(const Exception&)
486 DBG_ERROR( "ODataSource::remove: caught an exception while creating the data source!" );
490 //---------------------------------------------------------------------
491 sal_Bool ODataSource::rename( const ::rtl::OUString& _rName ) SAL_THROW (( ))
493 if (!isValid())
494 // nothing to do
495 return sal_False;
497 m_pImpl->sName = _rName;
498 return sal_True;
501 //---------------------------------------------------------------------
502 ::rtl::OUString ODataSource::getName() const SAL_THROW (( ))
504 if ( !isValid() )
505 return ::rtl::OUString();
506 return m_pImpl->sName;
509 //---------------------------------------------------------------------
510 bool ODataSource::hasTable( const ::rtl::OUString& _rTableName ) const
512 if ( !isConnected() )
513 return false;
515 const StringBag& aTables( getTableNames() );
516 return aTables.find( _rTableName ) != aTables.end();
519 //---------------------------------------------------------------------
520 const StringBag& ODataSource::getTableNames() const SAL_THROW (( ))
522 m_pImpl->aTables.clear();
523 if ( !isConnected() )
525 DBG_ERROR( "ODataSource::getTableNames: not connected!" );
527 else
531 // get the tables container from the connection
532 Reference< XTablesSupplier > xSuppTables( m_pImpl->xConnection.getTyped(), UNO_QUERY );
533 Reference< XNameAccess > xTables;
534 if ( xSuppTables.is( ) )
535 xTables = xSuppTables->getTables();
536 DBG_ASSERT( xTables.is(), "ODataSource::getTableNames: could not retrieve the tables container!" );
538 // get the names
539 Sequence< ::rtl::OUString > aTableNames;
540 if ( xTables.is( ) )
541 aTableNames = xTables->getElementNames( );
543 // copy the names
544 const ::rtl::OUString* pTableNames = aTableNames.getConstArray();
545 const ::rtl::OUString* pTableNamesEnd = pTableNames + aTableNames.getLength();
546 for (;pTableNames < pTableNamesEnd; ++pTableNames)
547 m_pImpl->aTables.insert( *pTableNames );
549 catch(const Exception&)
554 // now the table cache is up-to-date
555 m_pImpl->bTablesUpToDate = sal_True;
556 return m_pImpl->aTables;
559 //---------------------------------------------------------------------
560 sal_Bool ODataSource::connect( Window* _pMessageParent ) SAL_THROW (( ))
562 if ( isConnected( ) )
563 // nothing to do
564 return sal_True;
566 // ................................................................
567 // create the interaction handler (needed for authentication and error handling)
568 static ::rtl::OUString s_sInteractionHandlerServiceName = ::rtl::OUString::createFromAscii("com.sun.star.sdb.InteractionHandler");
569 Reference< XInteractionHandler > xInteractions;
572 xInteractions = Reference< XInteractionHandler >(
573 m_pImpl->xORB->createInstance( s_sInteractionHandlerServiceName ),
574 UNO_QUERY
577 catch(const Exception&)
581 // ................................................................
582 // failure to create the interaction handler is a serious issue ...
583 if (!xInteractions.is())
585 if ( _pMessageParent )
586 ShowServiceNotAvailableError( _pMessageParent, s_sInteractionHandlerServiceName, sal_True );
587 return sal_False;
590 // ................................................................
591 // open the connection
592 Any aError;
593 Reference< XConnection > xConnection;
596 Reference< XCompletedConnection > xComplConn( m_pImpl->xDataSource, UNO_QUERY );
597 DBG_ASSERT( xComplConn.is(), "ODataSource::connect: missing the XCompletedConnection interface on the data source!" );
598 if ( xComplConn.is() )
599 xConnection = xComplConn->connectWithCompletion( xInteractions );
601 catch( const SQLContext& e ) { aError <<= e; }
602 catch( const SQLWarning& e ) { aError <<= e; }
603 catch( const SQLException& e ) { aError <<= e; }
604 catch( const Exception& )
606 DBG_ERROR( "ODataSource::connect: caught a generic exception!" );
609 // ................................................................
610 // handle errors
611 if ( aError.hasValue() && _pMessageParent )
615 SQLException aException;
616 aError >>= aException;
617 if ( !aException.Message.getLength() )
619 // prepend some context info
620 SQLContext aDetailedError;
621 aDetailedError.Message = String( ModuleRes( RID_STR_NOCONNECTION ) );
622 aDetailedError.Details = String( ModuleRes( RID_STR_PLEASECHECKSETTINGS ) );
623 aDetailedError.NextException = aError;
624 // handle (aka display) the new context info
625 xInteractions->handle( new OInteractionRequest( makeAny( aDetailedError ) ) );
627 else
629 // handle (aka display) the original error
630 xInteractions->handle( new OInteractionRequest( makeAny( aException ) ) );
633 catch( const Exception& )
635 DBG_ERROR( "ODataSource::connect: caught an exception while trying to display the error!" );
639 if ( !xConnection.is() )
640 return sal_False;
642 // ................................................................
643 // success
644 m_pImpl->xConnection.reset( xConnection );
645 m_pImpl->aTables.clear();
646 m_pImpl->bTablesUpToDate = sal_False;
648 return sal_True;
651 //---------------------------------------------------------------------
652 void ODataSource::disconnect( ) SAL_THROW (( ))
654 m_pImpl->xConnection.clear();
655 m_pImpl->aTables.clear();
656 m_pImpl->bTablesUpToDate = sal_False;
659 //---------------------------------------------------------------------
660 sal_Bool ODataSource::isConnected( ) const SAL_THROW (( ))
662 return m_pImpl->xConnection.is();
665 //---------------------------------------------------------------------
666 sal_Bool ODataSource::isValid() const SAL_THROW (( ))
668 return m_pImpl && m_pImpl->xDataSource.is();
670 //---------------------------------------------------------------------
671 Reference< XPropertySet > ODataSource::getDataSource() const SAL_THROW (( ))
673 return m_pImpl ? m_pImpl->xDataSource : Reference< XPropertySet >();
676 //.........................................................................
677 } // namespace abp
678 //.........................................................................