LibreOffice Module dbaccess (master) 1
dbloader2.cxx
Go to the documentation of this file.
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 <strings.hxx>
21
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>
46
50#include <comphelper/types.hxx>
54#include <sfx2/docfile.hxx>
55#include <unotools/fcm.hxx>
58#include <vcl/svapp.hxx>
59
60using namespace ::ucbhelper;
61using namespace ::com::sun::star::task;
62using namespace ::com::sun::star::uno;
63using namespace ::com::sun::star::ucb;
64using namespace ::com::sun::star::io;
65using namespace ::com::sun::star::util;
66using namespace ::com::sun::star::frame;
67using namespace ::com::sun::star::beans;
68using namespace ::com::sun::star::container;
69using namespace ::com::sun::star::lang;
70using namespace ::com::sun::star::document;
71using namespace ::com::sun::star::sdb;
72using namespace ::com::sun::star::embed;
73using namespace ::com::sun::star::ui::dialogs;
74using ::com::sun::star::awt::XWindow;
75using ::com::sun::star::sdb::application::NamedDatabaseObject;
76
77namespace dbaxml
78{
79
80namespace {
81
82class DBTypeDetection : public ::cppu::WeakImplHelper< XExtendedFilterDetection, XServiceInfo>
83{
84 const Reference< XComponentContext > m_aContext;
85
86public:
87 explicit DBTypeDetection(const Reference< XComponentContext >&);
88
89 // XServiceInfo
90 OUString SAL_CALL getImplementationName() override;
91 sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
92 Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
93
94 virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor ) override;
95};
96
97}
98
99DBTypeDetection::DBTypeDetection(const Reference< XComponentContext >& _rxContext)
100 :m_aContext( _rxContext )
101{
102}
103
104OUString SAL_CALL DBTypeDetection::detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor )
105{
106 try
107 {
108 ::comphelper::NamedValueCollection aMedia( Descriptor );
109 bool bStreamFromDescr = false;
110 OUString sURL = aMedia.getOrDefault( "URL", OUString() );
111
112 Reference< XInputStream > xInStream( aMedia.getOrDefault( "InputStream", Reference< XInputStream >() ) );
113 Reference< XPropertySet > xStorageProperties;
114 if ( xInStream.is() )
115 {
116 bStreamFromDescr = true;
118 xInStream, m_aContext ), UNO_QUERY );
119 }
120 else
121 {
122 OUString sSalvagedURL( aMedia.getOrDefault( "SalvagedFile", OUString() ) );
123
124 OUString sFileLocation( sSalvagedURL.isEmpty() ? sURL : sSalvagedURL );
125 if ( !sFileLocation.isEmpty() )
126 {
128 sFileLocation, ElementModes::READ, m_aContext ), UNO_QUERY );
129 }
130 }
131
132 if ( xStorageProperties.is() )
133 {
134 OUString sMediaType;
135 xStorageProperties->getPropertyValue( INFO_MEDIATYPE ) >>= sMediaType;
136 if ( sMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII || sMediaType == MIMETYPE_VND_SUN_XML_BASE_ASCII )
137 {
138 if ( bStreamFromDescr && !sURL.startsWith( "private:stream" ) )
139 {
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;
145 try
146 {
147 ::comphelper::disposeComponent(xStorageProperties);
148 if ( xInStream.is() )
149 xInStream->closeInput();
150 }
151 catch( Exception& )
152 {
153 DBG_UNHANDLED_EXCEPTION("dbaccess");
154 }
155 }
156
157 return "StarBase";
158 }
159 ::comphelper::disposeComponent(xStorageProperties);
160 }
161 } catch(Exception&){}
162 return OUString();
163}
164
165// XServiceInfo
166OUString SAL_CALL DBTypeDetection::getImplementationName()
167{
168 return "org.openoffice.comp.dbflt.DBTypeDetection";
169}
170
171// XServiceInfo
172sal_Bool SAL_CALL DBTypeDetection::supportsService(const OUString& ServiceName)
173{
174 return cppu::supportsService(this, ServiceName);
175}
176
177// XServiceInfo
178Sequence< OUString > SAL_CALL DBTypeDetection::getSupportedServiceNames()
179{
180 return { "com.sun.star.document.ExtendedTypeDetection" };
181}
182
183} // namespace dbaxml
184
185extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
187 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
188{
189 return cppu::acquire(new ::dbaxml::DBTypeDetection(context));
190}
191
192namespace dbaxml
193{
194
195namespace {
196
197class DBContentLoader : public ::cppu::WeakImplHelper< XFrameLoader, XServiceInfo>
198{
199private:
200 const Reference< XComponentContext > m_aContext;
201 Reference< XFrameLoader > m_xMySelf;
204
205 DECL_LINK( OnStartTableWizard, void*, void );
206public:
207 explicit DBContentLoader(const Reference< XComponentContext >&);
208
209 // XServiceInfo
210 OUString SAL_CALL getImplementationName() override;
211 sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
212 Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
213
214 // XLoader
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;
219
220private:
221 bool impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard );
222};
223
224}
225
226DBContentLoader::DBContentLoader(const Reference< XComponentContext >& _rxFactory)
227 :m_aContext( _rxFactory )
228 ,m_nStartWizard(nullptr)
229{
230
231}
232
233// XServiceInfo
234OUString SAL_CALL DBContentLoader::getImplementationName()
235{
236 return "org.openoffice.comp.dbflt.DBContentLoader2";
237}
238
239// XServiceInfo
240sal_Bool SAL_CALL DBContentLoader::supportsService(const OUString& ServiceName)
241{
242 return cppu::supportsService(this, ServiceName);
243}
244
245// XServiceInfo
246Sequence< OUString > SAL_CALL DBContentLoader::getSupportedServiceNames()
247{
248 return { "com.sun.star.frame.FrameLoader" };
249}
250
251
252namespace
253{
254 bool lcl_urlAllowsInteraction( const Reference<XComponentContext> & _rContext, const OUString& _rURL )
255 {
256 bool bDoesAllow = false;
257 try
258 {
259 Reference< XURLTransformer > xTransformer( URLTransformer::create(_rContext) );
260 URL aURL;
261 aURL.Complete = _rURL;
262 xTransformer->parseStrict( aURL );
263 bDoesAllow = aURL.Arguments == "Interactive";
264 }
265 catch( const Exception& )
266 {
267 TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_urlAllowsInteraction: caught an exception while analyzing the URL!" );
268 }
269 return bDoesAllow;
270 }
271
272 Reference< XWindow > lcl_getTopMostWindow( const Reference<XComponentContext> & _rxContext )
273 {
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() )
279 {
280 xWindow = xActiveFrame->getContainerWindow();
281 Reference<XFrame> xFrame = xActiveFrame;
282 while ( xFrame.is() && !xFrame->isTop() )
283 xFrame = xFrame->getCreator();
284
285 if ( xFrame.is() )
286 xWindow = xFrame->getContainerWindow();
287 }
288 return xWindow;
289 }
290}
291
292bool DBContentLoader::impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard )
293{
294 Sequence<Any> aWizardArgs(comphelper::InitAnyPropertySequence(
295 {
296 {"ParentWindow", Any(lcl_getTopMostWindow( m_aContext ))},
297 {"InitialSelection", Any(_rxModel)}
298 }));
299
300 // create the dialog
301 Reference< XExecutableDialog > xAdminDialog( m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.sdb.DatabaseWizardDialog", aWizardArgs, m_aContext), UNO_QUERY_THROW);
302
303 // execute it
304 if ( RET_OK != xAdminDialog->execute() )
305 return false;
306
307 Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
308 bool bSuccess = false;
309 xProp->getPropertyValue("OpenDatabase") >>= bSuccess;
310 xProp->getPropertyValue("StartTableWizard") >>= _bShouldStartTableWizard;
311 return bSuccess;
312}
313
314void SAL_CALL DBContentLoader::load(const Reference< XFrame > & rFrame, const OUString& _rURL,
315 const Sequence< PropertyValue >& rArgs,
316 const Reference< XLoadEventListener > & rListener)
317{
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 );
321 if ( bPreview )
322 {
323 if (rListener.is())
324 rListener->loadCancelled(this);
325 return;
326 }
327
328 Reference< XModel > xModel = aMediaDesc.getOrDefault( "Model", Reference< XModel >() );
329 OUString sSalvagedURL = aMediaDesc.getOrDefault( "SalvagedFile", _rURL );
330
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?
333
334 bool bSuccess = true;
335
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
341 // not touch it.
342 if ( !aMediaDesc.has( "InteractionHandler" ) )
343 {
344 Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) );
345 aMediaDesc.put( "InteractionHandler", xHandler );
346 }
347
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" );
352
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" );
356
357 // this needs to stay alive for duration of this method
358 Reference< XDatabaseContext > xDatabaseContext;
359
360 sal_Int32 nInitialSelection = -1;
361 if ( !xModel.is() )
362 {
363 xDatabaseContext = DatabaseContext::create(m_aContext);
364
366 bCreateNew = sFactoryName.match(_rURL);
367
368 Reference< XDocumentDataSource > xDocumentDataSource;
369 bool bNewAndInteractive = false;
370 if ( bCreateNew )
371 {
372 bNewAndInteractive = lcl_urlAllowsInteraction( m_aContext, _rURL );
373 xDocumentDataSource.set( xDatabaseContext->createInstance(), UNO_QUERY_THROW );
374 }
375 else
376 {
378 aCreationArgs.put( INFO_POOLURL, sSalvagedURL );
379 xDocumentDataSource.set( xDatabaseContext->createInstanceWithArguments( aCreationArgs.getWrappedNamedValues() ), UNO_QUERY_THROW );
380 }
381
382 xModel.set( xDocumentDataSource->getDatabaseDocument(), UNO_QUERY );
383
384 if ( bCreateNew && xModel.is() )
385 {
386 if ( bNewAndInteractive )
387 {
388 bSuccess = impl_executeNewDatabaseWizard( xModel, bStartTableWizard );
389 }
390 else
391 {
392 try
393 {
394 Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW );
395 xLoad->initNew();
396 bSuccess = true;
397 }
398 catch( const Exception& )
399 {
400 bSuccess = false;
401 }
402 }
403
404 // initially select the "Tables" category (will be done below)
405 nInitialSelection = css::sdb::application::DatabaseObjectContainer::TABLES;
406 }
407 }
408
409 if ( !xModel.is() )
410 {
411 if ( rListener.is() )
412 rListener->loadCancelled(this);
413 return;
414 }
415
416 if ( !bCreateNew )
417 {
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();
423 try
424 {
425 aMediaDesc.put( "FileName", _rURL );
426 Sequence< PropertyValue > aResource( aMediaDesc.getPropertyValues() );
427
428 if ( bNeedLoad )
429 {
430 Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW );
431 xLoad->load( aResource );
432 }
433
434 // always attach the resource, even if the document has not been freshly loaded
435 xModel->attachResource( _rURL, aResource );
436 }
437 catch(const Exception&)
438 {
439 DBG_UNHANDLED_EXCEPTION("dbaccess");
440 bSuccess = false;
441 }
442 }
443
444 if ( bSuccess )
445 {
446 try
447 {
448 Reference< XModel2 > xModel2( xModel, UNO_QUERY_THROW );
449 Reference< XController2 > xController( xModel2->createViewController( sViewName, Sequence< PropertyValue >(), rFrame ), UNO_SET_THROW );
450
451 // introduce model/view/controller to each other
453
454 bSuccess = true;
455 }
456 catch( const Exception& )
457 {
458 DBG_UNHANDLED_EXCEPTION("dbaccess");
459 bSuccess = false;
460 }
461 }
462
463 if (bSuccess)
464 {
465 if ( rListener.is() )
466 rListener->loadFinished(this);
467
468 if ( nInitialSelection != -1 )
469 {
470 Reference< css::view::XSelectionSupplier > xDocView( xModel->getCurrentController(), UNO_QUERY );
471 if ( xDocView.is() )
472 {
473 NamedDatabaseObject aSelection;
474 aSelection.Type = nInitialSelection;
475 xDocView->select( Any( aSelection ) );
476 }
477 }
478
479 if ( bStartTableWizard )
480 {
481 // reset the data of the previous async drop (if any)
482 if ( m_nStartWizard )
484 m_sCurrentURL = xModel->getURL();
485 m_xMySelf = this;
486 m_nStartWizard = Application::PostUserEvent(LINK(this, DBContentLoader, OnStartTableWizard));
487 }
488 }
489 else
490 {
491 if ( rListener.is() )
492 rListener->loadCancelled( this );
493 }
494
495 if ( !bSuccess )
496 ::comphelper::disposeComponent(xModel);
497}
498
499void DBContentLoader::cancel()
500{
501}
502
503IMPL_LINK_NOARG( DBContentLoader, OnStartTableWizard, void*, void )
504{
505 m_nStartWizard = nullptr;
506 try
507 {
508 Sequence<Any> aWizArgs(comphelper::InitAnyPropertySequence(
509 {
510 {"DatabaseLocation", Any(m_sCurrentURL)}
511 }));
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");
516 }
517 catch(const Exception&)
518 {
519 TOOLS_WARN_EXCEPTION( "dbaccess", "caught an exception while starting the table wizard!");
520 }
521 m_xMySelf = nullptr;
522}
523
524}
525
526extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
528 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
529{
530 return cppu::acquire(new ::dbaxml::DBContentLoader(context));
531}
532
533/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr OUStringLiteral sMediaType
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
static void RemoveUserEvent(ImplSVEvent *nUserEvent)
OUString GetFactoryEmptyDocumentURL(EFactory eFactory) const
css::uno::Sequence< css::uno::Any > getWrappedNamedValues() const
bool put(const OUString &_rValueName, const VALUE_TYPE &_rValue)
static css::uno::Reference< css::embed::XStorage > GetStorageFromInputStream(const css::uno::Reference< css::io::XInputStream > &xStream, const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
static css::uno::Reference< css::embed::XStorage > GetStorageFromURL(const OUString &aURL, sal_Int32 nStorageMode, const css::uno::Reference< css::uno::XComponentContext > &rxContext=css::uno::Reference< css::uno::XComponentContext >())
const Reference< XComponentContext > m_aContext
Definition: dbloader2.cxx:84
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * org_openoffice_comp_dbflt_DBTypeDetection_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
Definition: dbloader2.cxx:186
ImplSVEvent * m_nStartWizard
Definition: dbloader2.cxx:203
OUString m_sCurrentURL
Definition: dbloader2.cxx:202
Reference< XFrameLoader > m_xMySelf
Definition: dbloader2.cxx:201
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * org_openoffice_comp_dbflt_DBContentLoader2_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
Definition: dbloader2.cxx:527
#define TOOLS_WARN_EXCEPTION(area, stream)
#define DBG_UNHANDLED_EXCEPTION(...)
DECL_LINK(CheckNameHdl, SvxNameDialog &, bool)
URL aURL
Definition: intercept.cxx:87
@ Exception
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
IMPL_LINK_NOARG(DBContentLoader, OnStartTableWizard, void *, void)
Definition: dbloader2.cxx:503
void ConnectFrameControllerModel(const css::uno::Reference< css::frame::XFrame > &xFrame, const css::uno::Reference< css::frame::XController2 > &xController, const css::uno::Reference< css::frame::XModel > &xModel)
constexpr OUStringLiteral INFO_MEDIATYPE
Definition: strings.hxx:229
constexpr OUStringLiteral INFO_POOLURL
Definition: strings.hxx:233
Reference< XController > xController
the controller of the sub component. Must not be <NULL>
Reference< XFrame > xFrame
the frame which the component resides in. Must not be <NULL>
Reference< XModel > xModel
the model of the sub component. Might be <NULL>
unsigned char sal_Bool