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 .
20 #include <strings.hxx>
22 #include <com/sun/star/beans/NamedValue.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/document/XExtendedFilterDetection.hpp>
25 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/embed/XStorage.hpp>
27 #include <com/sun/star/frame/Desktop.hpp>
28 #include <com/sun/star/frame/XController2.hpp>
29 #include <com/sun/star/frame/XFrame.hpp>
30 #include <com/sun/star/frame/XFrameLoader.hpp>
31 #include <com/sun/star/frame/XLoadEventListener.hpp>
32 #include <com/sun/star/frame/XModel2.hpp>
33 #include <com/sun/star/io/XInputStream.hpp>
34 #include <com/sun/star/lang/XServiceInfo.hpp>
35 #include <com/sun/star/sdb/DatabaseContext.hpp>
36 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
37 #include <com/sun/star/task/XJobExecutor.hpp>
38 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
39 #include <com/sun/star/task/InteractionHandler.hpp>
40 #include <com/sun/star/util/URLTransformer.hpp>
41 #include <com/sun/star/util/XURLTransformer.hpp>
42 #include <com/sun/star/view/XSelectionSupplier.hpp>
43 #include <com/sun/star/sdb/application/DatabaseObjectContainer.hpp>
44 #include <com/sun/star/sdb/application/NamedDatabaseObject.hpp>
45 #include <com/sun/star/frame/XLoadable.hpp>
47 #include <comphelper/documentconstants.hxx>
48 #include <comphelper/namedvaluecollection.hxx>
49 #include <comphelper/storagehelper.hxx>
50 #include <comphelper/types.hxx>
51 #include <comphelper/propertysequence.hxx>
52 #include <cppuhelper/implbase.hxx>
53 #include <cppuhelper/supportsservice.hxx>
54 #include <sfx2/docfile.hxx>
55 #include <unotools/fcm.hxx>
56 #include <unotools/moduleoptions.hxx>
57 #include <comphelper/diagnose_ex.hxx>
58 #include <vcl/svapp.hxx>
60 using namespace ::ucbhelper
;
61 using namespace ::com::sun::star::task
;
62 using namespace ::com::sun::star::uno
;
63 using namespace ::com::sun::star::ucb
;
64 using namespace ::com::sun::star::io
;
65 using namespace ::com::sun::star::util
;
66 using namespace ::com::sun::star::frame
;
67 using namespace ::com::sun::star::beans
;
68 using namespace ::com::sun::star::container
;
69 using namespace ::com::sun::star::lang
;
70 using namespace ::com::sun::star::document
;
71 using namespace ::com::sun::star::sdb
;
72 using namespace ::com::sun::star::embed
;
73 using namespace ::com::sun::star::ui::dialogs
;
74 using ::com::sun::star::awt::XWindow
;
75 using ::com::sun::star::sdb::application::NamedDatabaseObject
;
82 class DBTypeDetection
: public ::cppu::WeakImplHelper
< XExtendedFilterDetection
, XServiceInfo
>
84 const Reference
< XComponentContext
> m_aContext
;
87 explicit DBTypeDetection(const Reference
< XComponentContext
>&);
90 OUString SAL_CALL
getImplementationName() override
;
91 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
92 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
94 virtual OUString SAL_CALL
detect( css::uno::Sequence
< css::beans::PropertyValue
>& Descriptor
) override
;
99 DBTypeDetection::DBTypeDetection(const Reference
< XComponentContext
>& _rxContext
)
100 :m_aContext( _rxContext
)
104 OUString SAL_CALL
DBTypeDetection::detect( css::uno::Sequence
< css::beans::PropertyValue
>& Descriptor
)
108 ::comphelper::NamedValueCollection
aMedia( Descriptor
);
109 bool bStreamFromDescr
= false;
110 OUString sURL
= aMedia
.getOrDefault( "URL", OUString() );
112 Reference
< XInputStream
> xInStream( aMedia
.getOrDefault( "InputStream", Reference
< XInputStream
>() ) );
113 Reference
< XPropertySet
> xStorageProperties
;
114 if ( xInStream
.is() )
116 bStreamFromDescr
= true;
117 xStorageProperties
.set( ::comphelper::OStorageHelper::GetStorageFromInputStream(
118 xInStream
, m_aContext
), UNO_QUERY
);
122 OUString
sSalvagedURL( aMedia
.getOrDefault( "SalvagedFile", OUString() ) );
124 OUString
sFileLocation( sSalvagedURL
.isEmpty() ? sURL
: sSalvagedURL
);
125 if ( !sFileLocation
.isEmpty() )
127 xStorageProperties
.set( ::comphelper::OStorageHelper::GetStorageFromURL(
128 sFileLocation
, ElementModes::READ
, m_aContext
), UNO_QUERY
);
132 if ( xStorageProperties
.is() )
135 xStorageProperties
->getPropertyValue( INFO_MEDIATYPE
) >>= sMediaType
;
136 if ( sMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII
|| sMediaType
== MIMETYPE_VND_SUN_XML_BASE_ASCII
)
138 if ( bStreamFromDescr
&& !sURL
.startsWith( "private:stream" ) )
140 // After fixing of the i88522 issue ( use the new file locking for database files ) the stream from the type detection can be used further
141 // for now the file should be reopened to have read/write access
142 aMedia
.remove( "InputStream" );
143 aMedia
.remove( "Stream" );
144 aMedia
>>= Descriptor
;
147 ::comphelper::disposeComponent(xStorageProperties
);
148 if ( xInStream
.is() )
149 xInStream
->closeInput();
153 DBG_UNHANDLED_EXCEPTION("dbaccess");
159 ::comphelper::disposeComponent(xStorageProperties
);
161 } catch(Exception
&){}
166 OUString SAL_CALL
DBTypeDetection::getImplementationName()
168 return "org.openoffice.comp.dbflt.DBTypeDetection";
172 sal_Bool SAL_CALL
DBTypeDetection::supportsService(const OUString
& ServiceName
)
174 return cppu::supportsService(this, ServiceName
);
178 Sequence
< OUString
> SAL_CALL
DBTypeDetection::getSupportedServiceNames()
180 return { "com.sun.star.document.ExtendedTypeDetection" };
183 } // namespace dbaxml
185 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
186 org_openoffice_comp_dbflt_DBTypeDetection_get_implementation(
187 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
189 return cppu::acquire(new ::dbaxml::DBTypeDetection(context
));
197 class DBContentLoader
: public ::cppu::WeakImplHelper
< XFrameLoader
, XServiceInfo
>
200 const Reference
< XComponentContext
> m_aContext
;
201 Reference
< XFrameLoader
> m_xMySelf
;
202 OUString m_sCurrentURL
;
203 ImplSVEvent
* m_nStartWizard
;
205 DECL_LINK( OnStartTableWizard
, void*, void );
207 explicit DBContentLoader(const Reference
< XComponentContext
>&);
210 OUString SAL_CALL
getImplementationName() override
;
211 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
212 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
215 virtual void SAL_CALL
load( const Reference
< XFrame
> & _rFrame
, const OUString
& _rURL
,
216 const Sequence
< PropertyValue
>& _rArgs
,
217 const Reference
< XLoadEventListener
> & _rListener
) override
;
218 virtual void SAL_CALL
cancel() override
;
221 bool impl_executeNewDatabaseWizard( Reference
< XModel
> const & _rxModel
, bool& _bShouldStartTableWizard
);
226 DBContentLoader::DBContentLoader(const Reference
< XComponentContext
>& _rxFactory
)
227 :m_aContext( _rxFactory
)
228 ,m_nStartWizard(nullptr)
234 OUString SAL_CALL
DBContentLoader::getImplementationName()
236 return "org.openoffice.comp.dbflt.DBContentLoader2";
240 sal_Bool SAL_CALL
DBContentLoader::supportsService(const OUString
& ServiceName
)
242 return cppu::supportsService(this, ServiceName
);
246 Sequence
< OUString
> SAL_CALL
DBContentLoader::getSupportedServiceNames()
248 return { "com.sun.star.frame.FrameLoader" };
254 bool lcl_urlAllowsInteraction( const Reference
<XComponentContext
> & _rContext
, const OUString
& _rURL
)
256 bool bDoesAllow
= false;
259 Reference
< XURLTransformer
> xTransformer( URLTransformer::create(_rContext
) );
261 aURL
.Complete
= _rURL
;
262 xTransformer
->parseStrict( aURL
);
263 bDoesAllow
= aURL
.Arguments
== "Interactive";
265 catch( const Exception
& )
267 TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_urlAllowsInteraction: caught an exception while analyzing the URL!" );
272 Reference
< XWindow
> lcl_getTopMostWindow( const Reference
<XComponentContext
> & _rxContext
)
274 Reference
< XWindow
> xWindow
;
275 // get the top most window
276 Reference
< XDesktop2
> xDesktop
= Desktop::create(_rxContext
);
277 Reference
< XFrame
> xActiveFrame
= xDesktop
->getActiveFrame();
278 if ( xActiveFrame
.is() )
280 xWindow
= xActiveFrame
->getContainerWindow();
281 Reference
<XFrame
> xFrame
= xActiveFrame
;
282 while ( xFrame
.is() && !xFrame
->isTop() )
283 xFrame
= xFrame
->getCreator();
286 xWindow
= xFrame
->getContainerWindow();
292 bool DBContentLoader::impl_executeNewDatabaseWizard( Reference
< XModel
> const & _rxModel
, bool& _bShouldStartTableWizard
)
294 Sequence
<Any
> aWizardArgs(comphelper::InitAnyPropertySequence(
296 {"ParentWindow", Any(lcl_getTopMostWindow( m_aContext
))},
297 {"InitialSelection", Any(_rxModel
)}
301 Reference
< XExecutableDialog
> xAdminDialog( m_aContext
->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.sdb.DatabaseWizardDialog", aWizardArgs
, m_aContext
), UNO_QUERY_THROW
);
304 if ( RET_OK
!= xAdminDialog
->execute() )
307 Reference
<XPropertySet
> xProp(xAdminDialog
,UNO_QUERY
);
308 bool bSuccess
= false;
309 xProp
->getPropertyValue("OpenDatabase") >>= bSuccess
;
310 xProp
->getPropertyValue("StartTableWizard") >>= _bShouldStartTableWizard
;
314 void SAL_CALL
DBContentLoader::load(const Reference
< XFrame
> & rFrame
, const OUString
& _rURL
,
315 const Sequence
< PropertyValue
>& rArgs
,
316 const Reference
< XLoadEventListener
> & rListener
)
318 // first check if preview is true, if so return without creating a controller. Preview is not supported
319 ::comphelper::NamedValueCollection
aMediaDesc( rArgs
);
320 bool bPreview
= aMediaDesc
.getOrDefault( "Preview", false );
324 rListener
->loadCancelled(this);
328 Reference
< XModel
> xModel
= aMediaDesc
.getOrDefault( "Model", Reference
< XModel
>() );
329 OUString sSalvagedURL
= aMediaDesc
.getOrDefault( "SalvagedFile", _rURL
);
331 bool bCreateNew
= false; // does the URL denote the private:factory URL?
332 bool bStartTableWizard
= false; // start the table wizard after everything was loaded successfully?
334 bool bSuccess
= true;
336 // If there's no interaction handler in the media descriptor, put one.
337 // By definition, loading via loadComponentFromURL (and thus via the content loader here)
338 // is allowed to raise UI. To not burden every place inside the document with creating
339 // a default handler, we simply ensure there is one.
340 // If a handler is present in the media descriptor, even if it is NULL, we will
342 if ( !aMediaDesc
.has( "InteractionHandler" ) )
344 Reference
< XInteractionHandler2
> xHandler( InteractionHandler::createWithParent(m_aContext
, nullptr) );
345 aMediaDesc
.put( "InteractionHandler", xHandler
);
348 // it's allowed to pass an existing document
349 Reference
< XOfficeDatabaseDocument
> xExistentDBDoc
;
350 xModel
.set( aMediaDesc
.getOrDefault( "Model", xExistentDBDoc
), UNO_QUERY
);
351 aMediaDesc
.remove( "Model" );
353 // also, it's allowed to specify the type of view which should be created
354 OUString sViewName
= aMediaDesc
.getOrDefault( "ViewName", OUString( "Default" ) );
355 aMediaDesc
.remove( "ViewName" );
357 // this needs to stay alive for duration of this method
358 Reference
< XDatabaseContext
> xDatabaseContext
;
360 sal_Int32 nInitialSelection
= -1;
363 xDatabaseContext
= DatabaseContext::create(m_aContext
);
365 OUString sFactoryName
= SvtModuleOptions().GetFactoryEmptyDocumentURL(SvtModuleOptions::EFactory::DATABASE
);
366 bCreateNew
= sFactoryName
.match(_rURL
);
368 Reference
< XDocumentDataSource
> xDocumentDataSource
;
369 bool bNewAndInteractive
= false;
372 bNewAndInteractive
= lcl_urlAllowsInteraction( m_aContext
, _rURL
);
373 xDocumentDataSource
.set( xDatabaseContext
->createInstance(), UNO_QUERY_THROW
);
377 ::comphelper::NamedValueCollection aCreationArgs
;
378 aCreationArgs
.put( INFO_POOLURL
, sSalvagedURL
);
379 xDocumentDataSource
.set( xDatabaseContext
->createInstanceWithArguments( aCreationArgs
.getWrappedNamedValues() ), UNO_QUERY_THROW
);
382 xModel
.set( xDocumentDataSource
->getDatabaseDocument(), UNO_QUERY
);
384 if ( bCreateNew
&& xModel
.is() )
386 if ( bNewAndInteractive
)
388 bSuccess
= impl_executeNewDatabaseWizard( xModel
, bStartTableWizard
);
394 Reference
< XLoadable
> xLoad( xModel
, UNO_QUERY_THROW
);
398 catch( const Exception
& )
404 // initially select the "Tables" category (will be done below)
405 nInitialSelection
= css::sdb::application::DatabaseObjectContainer::TABLES
;
411 if ( rListener
.is() )
412 rListener
->loadCancelled(this);
418 // We need to XLoadable::load the document if it does not yet have a URL.
419 // If it already *does* have a URL, then it was either passed in the arguments, or a previous incarnation
420 // of that model existed before (which can happen if a model is closed, but an associated DataSource is kept
421 // alive 'til loading the document again).
422 bool bNeedLoad
= xModel
->getURL().isEmpty();
425 aMediaDesc
.put( "FileName", _rURL
);
426 Sequence
< PropertyValue
> aResource( aMediaDesc
.getPropertyValues() );
430 Reference
< XLoadable
> xLoad( xModel
, UNO_QUERY_THROW
);
431 xLoad
->load( aResource
);
434 // always attach the resource, even if the document has not been freshly loaded
435 xModel
->attachResource( _rURL
, aResource
);
437 catch(const Exception
&)
439 DBG_UNHANDLED_EXCEPTION("dbaccess");
448 Reference
< XModel2
> xModel2( xModel
, UNO_QUERY_THROW
);
449 Reference
< XController2
> xController( xModel2
->createViewController( sViewName
, Sequence
< PropertyValue
>(), rFrame
), UNO_SET_THROW
);
451 // introduce model/view/controller to each other
452 utl::ConnectFrameControllerModel(rFrame
, xController
, xModel
);
456 catch( const Exception
& )
458 DBG_UNHANDLED_EXCEPTION("dbaccess");
465 if ( rListener
.is() )
466 rListener
->loadFinished(this);
468 if ( nInitialSelection
!= -1 )
470 Reference
< css::view::XSelectionSupplier
> xDocView( xModel
->getCurrentController(), UNO_QUERY
);
473 NamedDatabaseObject aSelection
;
474 aSelection
.Type
= nInitialSelection
;
475 xDocView
->select( Any( aSelection
) );
479 if ( bStartTableWizard
)
481 // reset the data of the previous async drop (if any)
482 if ( m_nStartWizard
)
483 Application::RemoveUserEvent(m_nStartWizard
);
484 m_sCurrentURL
= xModel
->getURL();
486 m_nStartWizard
= Application::PostUserEvent(LINK(this, DBContentLoader
, OnStartTableWizard
));
491 if ( rListener
.is() )
492 rListener
->loadCancelled( this );
496 ::comphelper::disposeComponent(xModel
);
499 void DBContentLoader::cancel()
503 IMPL_LINK_NOARG( DBContentLoader
, OnStartTableWizard
, void*, void )
505 m_nStartWizard
= nullptr;
508 Sequence
<Any
> aWizArgs(comphelper::InitAnyPropertySequence(
510 {"DatabaseLocation", Any(m_sCurrentURL
)}
512 SolarMutexGuard aGuard
;
513 Reference
< XJobExecutor
> xTableWizard( m_aContext
->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.wizards.table.CallTableWizard", aWizArgs
, m_aContext
), UNO_QUERY
);
514 if ( xTableWizard
.is() )
515 xTableWizard
->trigger("start");
517 catch(const Exception
&)
519 TOOLS_WARN_EXCEPTION( "dbaccess", "caught an exception while starting the table wizard!");
526 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
527 org_openoffice_comp_dbflt_DBContentLoader2_get_implementation(
528 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
530 return cppu::acquire(new ::dbaxml::DBContentLoader(context
));
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */