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/moduleoptions.hxx>
56 #include <tools/diagnose_ex.h>
57 #include <vcl/svapp.hxx>
59 using namespace ::ucbhelper
;
60 using namespace ::com::sun::star::task
;
61 using namespace ::com::sun::star::uno
;
62 using namespace ::com::sun::star::ucb
;
63 using namespace ::com::sun::star::io
;
64 using namespace ::com::sun::star::util
;
65 using namespace ::com::sun::star::frame
;
66 using namespace ::com::sun::star::beans
;
67 using namespace ::com::sun::star::container
;
68 using namespace ::com::sun::star::lang
;
69 using namespace ::com::sun::star::document
;
70 using namespace ::com::sun::star::sdb
;
71 using namespace ::com::sun::star::embed
;
72 using namespace ::com::sun::star::ui::dialogs
;
73 using ::com::sun::star::awt::XWindow
;
74 using ::com::sun::star::sdb::application::NamedDatabaseObject
;
81 class DBTypeDetection
: public ::cppu::WeakImplHelper
< XExtendedFilterDetection
, XServiceInfo
>
83 const Reference
< XComponentContext
> m_aContext
;
86 explicit DBTypeDetection(const Reference
< XComponentContext
>&);
89 OUString SAL_CALL
getImplementationName() override
;
90 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
91 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
93 virtual OUString SAL_CALL
detect( css::uno::Sequence
< css::beans::PropertyValue
>& Descriptor
) override
;
98 DBTypeDetection::DBTypeDetection(const Reference
< XComponentContext
>& _rxContext
)
99 :m_aContext( _rxContext
)
103 OUString SAL_CALL
DBTypeDetection::detect( css::uno::Sequence
< css::beans::PropertyValue
>& Descriptor
)
107 ::comphelper::NamedValueCollection
aMedia( Descriptor
);
108 bool bStreamFromDescr
= false;
109 OUString sURL
= aMedia
.getOrDefault( "URL", OUString() );
111 Reference
< XInputStream
> xInStream( aMedia
.getOrDefault( "InputStream", Reference
< XInputStream
>() ) );
112 Reference
< XPropertySet
> xStorageProperties
;
113 if ( xInStream
.is() )
115 bStreamFromDescr
= true;
116 xStorageProperties
.set( ::comphelper::OStorageHelper::GetStorageFromInputStream(
117 xInStream
, m_aContext
), UNO_QUERY
);
121 OUString
sSalvagedURL( aMedia
.getOrDefault( "SalvagedFile", OUString() ) );
123 OUString
sFileLocation( sSalvagedURL
.isEmpty() ? sURL
: sSalvagedURL
);
124 if ( !sFileLocation
.isEmpty() )
126 xStorageProperties
.set( ::comphelper::OStorageHelper::GetStorageFromURL(
127 sFileLocation
, ElementModes::READ
, m_aContext
), UNO_QUERY
);
131 if ( xStorageProperties
.is() )
134 xStorageProperties
->getPropertyValue( INFO_MEDIATYPE
) >>= sMediaType
;
135 if ( sMediaType
== MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII
|| sMediaType
== MIMETYPE_VND_SUN_XML_BASE_ASCII
)
137 if ( bStreamFromDescr
&& !sURL
.startsWith( "private:stream" ) )
139 // After fixing of the i88522 issue ( use the new file locking for database files ) the stream from the type detection can be used further
140 // for now the file should be reopened to have read/write access
141 aMedia
.remove( OUString( "InputStream" ) );
142 aMedia
.remove( OUString( "Stream" ) );
143 aMedia
>>= Descriptor
;
146 ::comphelper::disposeComponent(xStorageProperties
);
147 if ( xInStream
.is() )
148 xInStream
->closeInput();
152 DBG_UNHANDLED_EXCEPTION("dbaccess");
158 ::comphelper::disposeComponent(xStorageProperties
);
160 } catch(Exception
&){}
165 OUString SAL_CALL
DBTypeDetection::getImplementationName()
167 return "org.openoffice.comp.dbflt.DBTypeDetection";
171 sal_Bool SAL_CALL
DBTypeDetection::supportsService(const OUString
& ServiceName
)
173 return cppu::supportsService(this, ServiceName
);
177 Sequence
< OUString
> SAL_CALL
DBTypeDetection::getSupportedServiceNames()
179 return { "com.sun.star.document.ExtendedTypeDetection" };
182 } // namespace dbaxml
184 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
185 org_openoffice_comp_dbflt_DBTypeDetection_get_implementation(
186 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
188 return cppu::acquire(new ::dbaxml::DBTypeDetection(context
));
196 class DBContentLoader
: public ::cppu::WeakImplHelper
< XFrameLoader
, XServiceInfo
>
199 const Reference
< XComponentContext
> m_aContext
;
200 Reference
< XFrameLoader
> m_xMySelf
;
201 OUString m_sCurrentURL
;
202 ImplSVEvent
* m_nStartWizard
;
204 DECL_LINK( OnStartTableWizard
, void*, void );
206 explicit DBContentLoader(const Reference
< XComponentContext
>&);
209 OUString SAL_CALL
getImplementationName() override
;
210 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
211 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
214 virtual void SAL_CALL
load( const Reference
< XFrame
> & _rFrame
, const OUString
& _rURL
,
215 const Sequence
< PropertyValue
>& _rArgs
,
216 const Reference
< XLoadEventListener
> & _rListener
) override
;
217 virtual void SAL_CALL
cancel() override
;
220 bool impl_executeNewDatabaseWizard( Reference
< XModel
> const & _rxModel
, bool& _bShouldStartTableWizard
);
225 DBContentLoader::DBContentLoader(const Reference
< XComponentContext
>& _rxFactory
)
226 :m_aContext( _rxFactory
)
227 ,m_nStartWizard(nullptr)
233 OUString SAL_CALL
DBContentLoader::getImplementationName()
235 return "org.openoffice.comp.dbflt.DBContentLoader2";
239 sal_Bool SAL_CALL
DBContentLoader::supportsService(const OUString
& ServiceName
)
241 return cppu::supportsService(this, ServiceName
);
245 Sequence
< OUString
> SAL_CALL
DBContentLoader::getSupportedServiceNames()
247 return { "com.sun.star.frame.FrameLoader" };
253 bool lcl_urlAllowsInteraction( const Reference
<XComponentContext
> & _rContext
, const OUString
& _rURL
)
255 bool bDoesAllow
= false;
258 Reference
< XURLTransformer
> xTransformer( URLTransformer::create(_rContext
) );
260 aURL
.Complete
= _rURL
;
261 xTransformer
->parseStrict( aURL
);
262 bDoesAllow
= aURL
.Arguments
== "Interactive";
264 catch( const Exception
& )
266 OSL_FAIL( "lcl_urlAllowsInteraction: caught an exception while analyzing the URL!" );
271 Reference
< XWindow
> lcl_getTopMostWindow( const Reference
<XComponentContext
> & _rxContext
)
273 Reference
< XWindow
> xWindow
;
274 // get the top most window
275 Reference
< XDesktop2
> xDesktop
= Desktop::create(_rxContext
);
276 Reference
< XFrame
> xActiveFrame
= xDesktop
->getActiveFrame();
277 if ( xActiveFrame
.is() )
279 xWindow
= xActiveFrame
->getContainerWindow();
280 Reference
<XFrame
> xFrame
= xActiveFrame
;
281 while ( xFrame
.is() && !xFrame
->isTop() )
282 xFrame
= xFrame
->getCreator();
285 xWindow
= xFrame
->getContainerWindow();
291 bool DBContentLoader::impl_executeNewDatabaseWizard( Reference
< XModel
> const & _rxModel
, bool& _bShouldStartTableWizard
)
293 Sequence
<Any
> aWizardArgs(comphelper::InitAnyPropertySequence(
295 {"ParentWindow", Any(lcl_getTopMostWindow( m_aContext
))},
296 {"InitialSelection", Any(_rxModel
)}
300 Reference
< XExecutableDialog
> xAdminDialog( m_aContext
->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.sdb.DatabaseWizardDialog", aWizardArgs
, m_aContext
), UNO_QUERY_THROW
);
303 if ( RET_OK
!= xAdminDialog
->execute() )
306 Reference
<XPropertySet
> xProp(xAdminDialog
,UNO_QUERY
);
307 bool bSuccess
= false;
308 xProp
->getPropertyValue("OpenDatabase") >>= bSuccess
;
309 xProp
->getPropertyValue("StartTableWizard") >>= _bShouldStartTableWizard
;
313 void SAL_CALL
DBContentLoader::load(const Reference
< XFrame
> & rFrame
, const OUString
& _rURL
,
314 const Sequence
< PropertyValue
>& rArgs
,
315 const Reference
< XLoadEventListener
> & rListener
)
317 // first check if preview is true, if so return without creating a controller. Preview is not supported
318 ::comphelper::NamedValueCollection
aMediaDesc( rArgs
);
319 bool bPreview
= aMediaDesc
.getOrDefault( "Preview", false );
323 rListener
->loadCancelled(this);
327 Reference
< XModel
> xModel
= aMediaDesc
.getOrDefault( "Model", Reference
< XModel
>() );
328 OUString sSalvagedURL
= aMediaDesc
.getOrDefault( "SalvagedFile", _rURL
);
330 bool bCreateNew
= false; // does the URL denote the private:factory URL?
331 bool bStartTableWizard
= false; // start the table wizard after everything was loaded successfully?
333 bool bSuccess
= true;
335 // If there's no interaction handler in the media descriptor, put one.
336 // By definition, loading via loadComponentFromURL (and thus via the content loader here)
337 // is allowed to raise UI. To not burden every place inside the document with creating
338 // a default handler, we simply ensure there is one.
339 // If a handler is present in the media descriptor, even if it is NULL, we will
341 if ( !aMediaDesc
.has( "InteractionHandler" ) )
343 Reference
< XInteractionHandler2
> xHandler( InteractionHandler::createWithParent(m_aContext
, nullptr) );
344 aMediaDesc
.put( "InteractionHandler", xHandler
);
347 // it's allowed to pass an existing document
348 Reference
< XOfficeDatabaseDocument
> xExistentDBDoc
;
349 xModel
.set( aMediaDesc
.getOrDefault( "Model", xExistentDBDoc
), UNO_QUERY
);
350 aMediaDesc
.remove( "Model" );
352 // also, it's allowed to specify the type of view which should be created
353 OUString sViewName
= aMediaDesc
.getOrDefault( "ViewName", OUString( "Default" ) );
354 aMediaDesc
.remove( "ViewName" );
356 // this needs to stay alive for duration of this method
357 Reference
< XDatabaseContext
> xDatabaseContext
;
359 sal_Int32 nInitialSelection
= -1;
362 xDatabaseContext
= DatabaseContext::create(m_aContext
);
364 OUString sFactoryName
= SvtModuleOptions().GetFactoryEmptyDocumentURL(SvtModuleOptions::EFactory::DATABASE
);
365 bCreateNew
= sFactoryName
.match(_rURL
);
367 Reference
< XDocumentDataSource
> xDocumentDataSource
;
368 bool bNewAndInteractive
= false;
371 bNewAndInteractive
= lcl_urlAllowsInteraction( m_aContext
, _rURL
);
372 xDocumentDataSource
.set( xDatabaseContext
->createInstance(), UNO_QUERY_THROW
);
376 ::comphelper::NamedValueCollection aCreationArgs
;
377 aCreationArgs
.put( OUString(INFO_POOLURL
), sSalvagedURL
);
378 xDocumentDataSource
.set( xDatabaseContext
->createInstanceWithArguments( aCreationArgs
.getWrappedNamedValues() ), UNO_QUERY_THROW
);
381 xModel
.set( xDocumentDataSource
->getDatabaseDocument(), UNO_QUERY
);
383 if ( bCreateNew
&& xModel
.is() )
385 if ( bNewAndInteractive
)
387 bSuccess
= impl_executeNewDatabaseWizard( xModel
, bStartTableWizard
);
393 Reference
< XLoadable
> xLoad( xModel
, UNO_QUERY_THROW
);
397 catch( const Exception
& )
403 // initially select the "Tables" category (will be done below)
404 nInitialSelection
= css::sdb::application::DatabaseObjectContainer::TABLES
;
410 if ( rListener
.is() )
411 rListener
->loadCancelled(this);
417 // We need to XLoadable::load the document if it does not yet have a URL.
418 // If it already *does* have a URL, then it was either passed in the arguments, or a previous incarnation
419 // of that model existed before (which can happen if a model is closed, but an associated DataSource is kept
420 // alive 'til loading the document again).
421 bool bNeedLoad
= xModel
->getURL().isEmpty();
424 aMediaDesc
.put( "FileName", _rURL
);
425 Sequence
< PropertyValue
> aResource( aMediaDesc
.getPropertyValues() );
429 Reference
< XLoadable
> xLoad( xModel
, UNO_QUERY_THROW
);
430 xLoad
->load( aResource
);
433 // always attach the resource, even if the document has not been freshly loaded
434 xModel
->attachResource( _rURL
, aResource
);
436 catch(const Exception
&)
438 DBG_UNHANDLED_EXCEPTION("dbaccess");
447 Reference
< XModel2
> xModel2( xModel
, UNO_QUERY_THROW
);
448 Reference
< XController2
> xController( xModel2
->createViewController( sViewName
, Sequence
< PropertyValue
>(), rFrame
), UNO_SET_THROW
);
450 xController
->attachModel( xModel
);
451 xModel
->connectController( xController
.get() );
452 rFrame
->setComponent( xController
->getComponentWindow(), xController
.get() );
453 xController
->attachFrame( rFrame
);
454 xModel
->setCurrentController( xController
.get() );
458 catch( const Exception
& )
460 DBG_UNHANDLED_EXCEPTION("dbaccess");
467 if ( rListener
.is() )
468 rListener
->loadFinished(this);
470 if ( nInitialSelection
!= -1 )
472 Reference
< css::view::XSelectionSupplier
> xDocView( xModel
->getCurrentController(), UNO_QUERY
);
475 NamedDatabaseObject aSelection
;
476 aSelection
.Type
= nInitialSelection
;
477 xDocView
->select( makeAny( aSelection
) );
481 if ( bStartTableWizard
)
483 // reset the data of the previous async drop (if any)
484 if ( m_nStartWizard
)
485 Application::RemoveUserEvent(m_nStartWizard
);
486 m_sCurrentURL
= xModel
->getURL();
488 m_nStartWizard
= Application::PostUserEvent(LINK(this, DBContentLoader
, OnStartTableWizard
));
493 if ( rListener
.is() )
494 rListener
->loadCancelled( this );
498 ::comphelper::disposeComponent(xModel
);
501 void DBContentLoader::cancel()
505 IMPL_LINK_NOARG( DBContentLoader
, OnStartTableWizard
, void*, void )
507 m_nStartWizard
= nullptr;
510 Sequence
<Any
> aWizArgs(comphelper::InitAnyPropertySequence(
512 {"DatabaseLocation", Any(m_sCurrentURL
)}
514 SolarMutexGuard aGuard
;
515 Reference
< XJobExecutor
> xTableWizard( m_aContext
->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.wizards.table.CallTableWizard", aWizArgs
, m_aContext
), UNO_QUERY
);
516 if ( xTableWizard
.is() )
517 xTableWizard
->trigger("start");
519 catch(const Exception
&)
521 OSL_FAIL("caught an exception while starting the table wizard!");
528 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
529 org_openoffice_comp_dbflt_DBContentLoader2_get_implementation(
530 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const& )
532 return cppu::acquire(new ::dbaxml::DBContentLoader(context
));
535 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */