1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <strings.hrc>
22 #include "abptypes.hxx"
23 #include <componentmodule.hxx>
24 #include "datasourcehandling.hxx"
25 #include "addresssettings.hxx"
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/container/XNameAccess.hpp>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/frame/XStorable.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/sdb/DatabaseContext.hpp>
33 #include <com/sun/star/sdb/SQLContext.hpp>
34 #include <com/sun/star/sdb/XCompletedConnection.hpp>
35 #include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
36 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
37 #include <com/sun/star/sdbc/XConnection.hpp>
38 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
39 #include <com/sun/star/task/InteractionHandler.hpp>
40 #include <com/sun/star/uri/UriReferenceFactory.hpp>
41 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
43 #include <comphelper/interaction.hxx>
44 #include <comphelper/processfactory.hxx>
45 #include <tools/debug.hxx>
46 #include <comphelper/diagnose_ex.hxx>
47 #include <unotools/sharedunocomponent.hxx>
48 #include <vcl/stdtext.hxx>
49 #include <vcl/weld.hxx>
50 #include <sfx2/objsh.hxx>
51 #include <sfx2/docfile.hxx>
52 #include <sfx2/viewfrm.hxx>
53 #include <comphelper/propertysequence.hxx>
58 /// Returns the URL of this object shell.
59 OUString
lcl_getOwnURL(SfxObjectShell
const * pObjectShell
)
66 const INetURLObject
& rURLObject
= pObjectShell
->GetMedium()->GetURLObject();
67 aRet
= rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
77 using namespace ::utl
;
78 using namespace ::comphelper
;
79 using namespace ::com::sun::star
;
80 using namespace ::com::sun::star::uno
;
81 using namespace ::com::sun::star::lang
;
82 using namespace ::com::sun::star::sdb
;
83 using namespace ::com::sun::star::sdbc
;
84 using namespace ::com::sun::star::task
;
85 using namespace ::com::sun::star::beans
;
86 using namespace ::com::sun::star::sdbcx
;
87 using namespace ::com::sun::star::container
;
88 using namespace ::com::sun::star::frame
;
90 static Reference
< XDatabaseContext
> lcl_getDataSourceContext( const Reference
< XComponentContext
>& _rxContext
)
92 Reference
<XDatabaseContext
> xContext
= DatabaseContext::create(_rxContext
);
97 /// creates a new data source and inserts it into the context
98 static void lcl_implCreateAndInsert(
99 const Reference
< XComponentContext
>& _rxContext
, const OUString
& _rName
,
100 Reference
< XPropertySet
>& /* [out] */ _rxNewDataSource
)
103 // get the data source context
104 Reference
< XDatabaseContext
> xContext
= lcl_getDataSourceContext( _rxContext
);
106 DBG_ASSERT( !xContext
->hasByName( _rName
), "lcl_implCreateAndInsert: name already used!" );
109 // create a new data source
110 Reference
< XPropertySet
> xNewDataSource
;
112 xNewDataSource
.set( xContext
->createInstance(), UNO_QUERY
);
113 DBG_ASSERT( xNewDataSource
.is(), "lcl_implCreateAndInsert: could not create a new data source!" );
116 // insert the data source into the context
117 DBG_ASSERT( xContext
.is(), "lcl_implCreateAndInsert: missing an interface on the context (XNamingService)!" );
120 // xDynamicContext->registerObject( _rName, xNewDataSource );
121 _rxNewDataSource
= xNewDataSource
;
126 /// creates and inserts a data source, and sets its URL property to the string given
127 static ODataSource
lcl_implCreateAndSetURL(
128 const Reference
< XComponentContext
>& _rxORB
, const OUString
& _rName
,
129 const char* _pInitialAsciiURL
)
131 ODataSource
aReturn( _rxORB
);
134 // create the new data source
135 Reference
< XPropertySet
> xNewDataSource
;
136 lcl_implCreateAndInsert( _rxORB
, _rName
, xNewDataSource
);
139 // set the URL property
140 if (xNewDataSource
.is())
142 xNewDataSource
->setPropertyValue(
144 Any( OUString::createFromAscii( _pInitialAsciiURL
) )
148 aReturn
.setDataSource( xNewDataSource
, _rName
);
150 catch(const Exception
&)
152 TOOLS_WARN_EXCEPTION("extensions.abpilot",
153 "caught an exception while creating the data source!");
159 static void lcl_registerDataSource(
160 const Reference
< XComponentContext
>& _rxORB
, const OUString
& _sName
,
161 const OUString
& _sURL
)
163 OSL_ENSURE( !_sName
.isEmpty(), "lcl_registerDataSource: invalid name!" );
164 OSL_ENSURE( !_sURL
.isEmpty(), "lcl_registerDataSource: invalid URL!" );
167 Reference
< XDatabaseContext
> xRegistrations( DatabaseContext::create(_rxORB
) );
168 if ( xRegistrations
->hasRegisteredDatabase( _sName
) )
169 xRegistrations
->changeDatabaseLocation( _sName
, _sURL
);
171 xRegistrations
->registerDatabaseLocation( _sName
, _sURL
);
173 catch( const Exception
& )
175 DBG_UNHANDLED_EXCEPTION("extensions.abpilot");
179 struct ODataSourceContextImpl
181 Reference
< XComponentContext
> xORB
;
182 Reference
< XNameAccess
> xContext
; /// the UNO data source context
183 StringBag aDataSourceNames
; /// for quicker name checks (without the UNO overhead)
185 explicit ODataSourceContextImpl(const Reference
< XComponentContext
>& _rxORB
)
189 ODataSourceContextImpl(const ODataSourceContextImpl
&) = delete;
190 ODataSourceContextImpl
& operator=(const ODataSourceContextImpl
&) = delete;
193 ODataSourceContext::ODataSourceContext(const Reference
< XComponentContext
>& _rxORB
)
194 :m_pImpl( new ODataSourceContextImpl( _rxORB
) )
198 // create the UNO context
199 m_pImpl
->xContext
.set( lcl_getDataSourceContext( _rxORB
), UNO_QUERY_THROW
);
201 // collect the data source names
202 Sequence
< OUString
> aDSNames
= m_pImpl
->xContext
->getElementNames();
203 const OUString
* pDSNames
= aDSNames
.getConstArray();
204 const OUString
* pDSNamesEnd
= pDSNames
+ aDSNames
.getLength();
206 for ( ;pDSNames
!= pDSNamesEnd
; ++pDSNames
)
207 m_pImpl
->aDataSourceNames
.insert( *pDSNames
);
209 catch( const Exception
& )
211 TOOLS_WARN_EXCEPTION( "extensions.abpilot", "ODataSourceContext::ODataSourceContext" );
214 ODataSourceContext::~ODataSourceContext()
219 void ODataSourceContext::disambiguate(OUString
& _rDataSourceName
)
221 OUString
sCheck( _rDataSourceName
);
222 StringBag::const_iterator aPos
= m_pImpl
->aDataSourceNames
.find( sCheck
);
224 sal_Int32 nPostfix
= 1;
225 while ( ( m_pImpl
->aDataSourceNames
.end() != aPos
) && ( nPostfix
< 65535 ) )
226 { // there already is a data source with this name
227 sCheck
= _rDataSourceName
+ OUString::number( nPostfix
++ );
229 aPos
= m_pImpl
->aDataSourceNames
.find( sCheck
);
232 _rDataSourceName
= sCheck
;
236 void ODataSourceContext::getDataSourceNames( StringBag
& _rNames
) const
238 _rNames
= m_pImpl
->aDataSourceNames
;
241 ODataSource
ODataSourceContext::createNewThunderbird( const OUString
& _rName
)
243 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:thunderbird" );
247 ODataSource
ODataSourceContext::createNewEvolutionLdap( const OUString
& _rName
)
249 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:evolution:ldap" );
252 ODataSource
ODataSourceContext::createNewEvolutionGroupwise( const OUString
& _rName
)
254 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:evolution:groupwise" );
257 ODataSource
ODataSourceContext::createNewEvolution( const OUString
& _rName
)
259 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:evolution:local" );
263 ODataSource
ODataSourceContext::createNewKab( const OUString
& _rName
)
265 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:kab" );
269 ODataSource
ODataSourceContext::createNewMacab( const OUString
& _rName
)
271 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:address:macab" );
275 // tdf117101: Spreadsheet by default
276 ODataSource
ODataSourceContext::createNewOther( const OUString
& _rName
)
278 return lcl_implCreateAndSetURL( m_pImpl
->xORB
, _rName
, "sdbc:calc:" );
281 struct ODataSourceImpl
284 Reference
< XComponentContext
> xORB
; /// the service factory
285 Reference
< XPropertySet
> xDataSource
; /// the UNO data source
286 ::utl::SharedUNOComponent
< XConnection
>
288 StringBag aTables
; // the cached table names
291 explicit ODataSourceImpl(const Reference
< XComponentContext
>& _rxORB
)
298 ODataSource::ODataSource( const ODataSource
& _rSource
)
303 ODataSource
& ODataSource::operator=( const ODataSource
& _rSource
)
305 if( this != &_rSource
)
307 m_pImpl
.reset( new ODataSourceImpl( *_rSource
.m_pImpl
) );
312 ODataSource
& ODataSource::operator=(ODataSource
&& _rSource
) noexcept
314 m_pImpl
= std::move(_rSource
.m_pImpl
);
318 ODataSource::ODataSource( const Reference
< XComponentContext
>& _rxORB
)
319 :m_pImpl(new ODataSourceImpl(_rxORB
))
323 ODataSource::~ODataSource( )
327 void ODataSource::store(const AddressSettings
& rSettings
)
334 Reference
< XDocumentDataSource
> xDocAccess( m_pImpl
->xDataSource
, UNO_QUERY
);
335 Reference
< XStorable
> xStorable
;
336 if ( xDocAccess
.is() )
337 xStorable
.set(xDocAccess
->getDatabaseDocument(), css::uno::UNO_QUERY
);
338 OSL_ENSURE( xStorable
.is(),"DataSource is no XStorable!" );
339 if ( xStorable
.is() )
341 SfxViewFrame
* pFrame
= SfxViewFrame::Current();
342 SfxObjectShell
* pObjectShell
= pFrame
? pFrame
->GetObjectShell() : nullptr;
343 OUString aOwnURL
= lcl_getOwnURL(pObjectShell
); // empty if pObjectShell is nullptr
344 if (aOwnURL
.isEmpty() || !rSettings
.bEmbedDataSource
)
346 // Cannot or should not embed.
347 xStorable
->storeAsURL(m_pImpl
->sName
,Sequence
<PropertyValue
>());
352 OUString aStreamRelPath
= "EmbeddedDatabase";
353 auto xContext(comphelper::getProcessComponentContext());
354 auto xUri
= css::uri::UriReferenceFactory::create(xContext
)->parse(aOwnURL
);
356 xUri
= css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext
)->createVndSunStarPkgUrlReference(xUri
);
358 OUString
const sTmpName
= xUri
->getUriReference() + "/" + aStreamRelPath
;
359 assert(pObjectShell
);
360 uno::Reference
<embed::XStorage
> xStorage
= pObjectShell
->GetStorage();
361 uno::Sequence
<beans::PropertyValue
> aSequence
= comphelper::InitPropertySequence(
363 {"TargetStorage", uno::Any(xStorage
)},
364 {"StreamRelPath", uno::Any(aStreamRelPath
)},
365 {"BaseURI", uno::Any(aOwnURL
)}
367 xStorable
->storeAsURL(sTmpName
, aSequence
);
368 m_pImpl
->sName
= sTmpName
;
370 // Refer to the sub-storage name in the document settings, so
371 // we can load it again next time the file is imported.
372 uno::Reference
<lang::XMultiServiceFactory
> xFactory(pObjectShell
->GetModel(), uno::UNO_QUERY
);
373 uno::Reference
<beans::XPropertySet
> xPropertySet(xFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
374 xPropertySet
->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath
));
378 catch(const Exception
&)
380 TOOLS_WARN_EXCEPTION("extensions.abpilot",
381 "caught an exception while creating the data source!");
385 void ODataSource::registerDataSource( const OUString
& _sRegisteredDataSourceName
)
393 // invalidate ourself
394 lcl_registerDataSource(m_pImpl
->xORB
,_sRegisteredDataSourceName
,m_pImpl
->sName
);
396 catch(const Exception
&)
398 TOOLS_WARN_EXCEPTION("extensions.abpilot",
399 "caught an exception while creating the data source!");
404 void ODataSource::setDataSource( const Reference
< XPropertySet
>& _rxDS
,const OUString
& _sName
)
406 if (m_pImpl
->xDataSource
.get() == _rxDS
.get())
413 m_pImpl
->sName
= _sName
;
414 m_pImpl
->xDataSource
= _rxDS
;
418 void ODataSource::remove()
426 // invalidate ourself
427 m_pImpl
->xDataSource
.clear();
429 catch(const Exception
&)
431 TOOLS_WARN_EXCEPTION("extensions.abpilot",
432 "caught an exception while creating the data source!");
437 bool ODataSource::rename( const OUString
& _rName
)
443 m_pImpl
->sName
= _rName
;
448 OUString
ODataSource::getName() const
452 return m_pImpl
->sName
;
456 bool ODataSource::hasTable( const OUString
& _rTableName
) const
458 if ( !isConnected() )
461 const StringBag
& aTables( getTableNames() );
462 return aTables
.find( _rTableName
) != aTables
.end();
466 const StringBag
& ODataSource::getTableNames() const
468 m_pImpl
->aTables
.clear();
469 if ( !isConnected() )
471 OSL_FAIL( "ODataSource::getTableNames: not connected!" );
477 // get the tables container from the connection
478 Reference
< XTablesSupplier
> xSuppTables( m_pImpl
->xConnection
.getTyped(), UNO_QUERY
);
479 Reference
< XNameAccess
> xTables
;
480 if ( xSuppTables
.is( ) )
481 xTables
= xSuppTables
->getTables();
482 DBG_ASSERT( xTables
.is(), "ODataSource::getTableNames: could not retrieve the tables container!" );
485 Sequence
< OUString
> aTableNames
;
487 aTableNames
= xTables
->getElementNames( );
490 const OUString
* pTableNames
= aTableNames
.getConstArray();
491 const OUString
* pTableNamesEnd
= pTableNames
+ aTableNames
.getLength();
492 for (;pTableNames
< pTableNamesEnd
; ++pTableNames
)
493 m_pImpl
->aTables
.insert( *pTableNames
);
495 catch(const Exception
&)
500 // now the table cache is up-to-date
501 return m_pImpl
->aTables
;
505 bool ODataSource::connect(weld::Window
* _pMessageParent
)
507 if ( isConnected( ) )
512 // create the interaction handler (needed for authentication and error handling)
513 Reference
< XInteractionHandler
> xInteractions
;
516 xInteractions
= InteractionHandler::createWithParent(m_pImpl
->xORB
, nullptr);
518 catch(const Exception
&)
523 // failure to create the interaction handler is a serious issue ...
524 if (!xInteractions
.is())
526 if ( _pMessageParent
)
527 ShowServiceNotAvailableError( _pMessageParent
, u
"com.sun.star.task.InteractionHandler", true );
532 // open the connection
534 Reference
< XConnection
> xConnection
;
537 Reference
< XCompletedConnection
> xComplConn( m_pImpl
->xDataSource
, UNO_QUERY
);
538 DBG_ASSERT( xComplConn
.is(), "ODataSource::connect: missing the XCompletedConnection interface on the data source!" );
539 if ( xComplConn
.is() )
540 xConnection
= xComplConn
->connectWithCompletion( xInteractions
);
542 catch( const SQLContext
& e
) { aError
<<= e
; }
543 catch( const SQLWarning
& e
) { aError
<<= e
; }
544 catch( const SQLException
& e
) { aError
<<= e
; }
545 catch( const Exception
& )
547 TOOLS_WARN_EXCEPTION("extensions.abpilot", "");
552 if ( aError
.hasValue() && _pMessageParent
)
556 SQLException aException
;
557 aError
>>= aException
;
558 if ( aException
.Message
.isEmpty() )
560 // prepend some context info
561 SQLContext
aDetailedError(compmodule::ModuleRes(RID_STR_NOCONNECTION
), // message
563 aError
, // next exception
564 compmodule::ModuleRes(RID_STR_PLEASECHECKSETTINGS
)); // details
565 // handle (aka display) the new context info
566 xInteractions
->handle( new OInteractionRequest( Any( aDetailedError
) ) );
570 // handle (aka display) the original error
571 xInteractions
->handle( new OInteractionRequest( Any( aException
) ) );
574 catch( const Exception
& )
576 TOOLS_WARN_EXCEPTION("extensions.abpilot",
577 "caught an exception while trying to display the error!");
581 if ( !xConnection
.is() )
586 m_pImpl
->xConnection
.reset( xConnection
);
587 m_pImpl
->aTables
.clear();
593 void ODataSource::disconnect( )
595 m_pImpl
->xConnection
.clear();
596 m_pImpl
->aTables
.clear();
600 bool ODataSource::isConnected( ) const
602 return m_pImpl
->xConnection
.is();
606 bool ODataSource::isValid() const
608 return m_pImpl
&& m_pImpl
->xDataSource
.is();
611 Reference
< XPropertySet
> ODataSource::getDataSource() const
613 return m_pImpl
? m_pImpl
->xDataSource
: Reference
< XPropertySet
>();
620 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */