/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <strings.hxx>
#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/document/XExtendedFilterDetection.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XController2.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/frame/XFrameLoader.hpp>
#include <com/sun/star/frame/XLoadEventListener.hpp>
#include <com/sun/star/frame/XModel2.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdb/XDocumentDataSource.hpp>
#include <com/sun/star/task/XJobExecutor.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/sdb/application/DatabaseObjectContainer.hpp>
#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp>
#include <com/sun/star/frame/XLoadable.hpp>
#include <comphelper/documentconstants.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/types.hxx>
#include <comphelper/propertysequence.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <sfx2/docfile.hxx>
#include <unotools/fcm.hxx>
#include <unotools/moduleoptions.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <vcl/svapp.hxx>
using namespace ::ucbhelper;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::document;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::embed;
using namespace ::com::sun::star::ui::dialogs;
using ::com::sun::star::awt::XWindow;
using ::com::sun::star::sdb::application::NamedDatabaseObject;
namespace dbaxml
{
namespace {
class DBTypeDetection : public ::cppu::WeakImplHelper< XExtendedFilterDetection, XServiceInfo>
{
const Reference< XComponentContext > m_aContext;
public:
explicit DBTypeDetection(const Reference< XComponentContext >&);
// XServiceInfo
OUString SAL_CALL getImplementationName() override;
sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor ) override;
};
}
DBTypeDetection::DBTypeDetection(const Reference< XComponentContext >& _rxContext)
:m_aContext( _rxContext )
{
}
OUString SAL_CALL DBTypeDetection::detect( css::uno::Sequence< css::beans::PropertyValue >& Descriptor )
{
try
{
::comphelper::NamedValueCollection aMedia( Descriptor );
bool bStreamFromDescr = false;
OUString sURL = aMedia.getOrDefault( u"URL"_ustr, OUString() );
Reference< XInputStream > xInStream( aMedia.getOrDefault( u"InputStream"_ustr, Reference< XInputStream >() ) );
Reference< XPropertySet > xStorageProperties;
if ( xInStream.is() )
{
bStreamFromDescr = true;
xStorageProperties.set( ::comphelper::OStorageHelper::GetStorageFromInputStream(
xInStream, m_aContext ), UNO_QUERY );
}
else
{
OUString sSalvagedURL( aMedia.getOrDefault( u"SalvagedFile"_ustr, OUString() ) );
OUString sFileLocation( sSalvagedURL.isEmpty() ? sURL : sSalvagedURL );
if ( !sFileLocation.isEmpty() )
{
xStorageProperties.set( ::comphelper::OStorageHelper::GetStorageFromURL(
sFileLocation, ElementModes::READ, m_aContext ), UNO_QUERY );
}
}
if ( xStorageProperties.is() )
{
OUString sMediaType;
xStorageProperties->getPropertyValue( INFO_MEDIATYPE ) >>= sMediaType;
if ( sMediaType == MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII || sMediaType == MIMETYPE_VND_SUN_XML_BASE_ASCII )
{
if ( bStreamFromDescr && !sURL.startsWith( "private:stream" ) )
{
// After fixing of the i88522 issue ( use the new file locking for database files ) the stream from the type detection can be used further
// for now the file should be reopened to have read/write access
aMedia.remove( u"InputStream"_ustr );
aMedia.remove( u"Stream"_ustr );
aMedia >>= Descriptor;
try
{
::comphelper::disposeComponent(xStorageProperties);
if ( xInStream.is() )
xInStream->closeInput();
}
catch( Exception& )
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
}
}
return u"StarBase"_ustr;
}
::comphelper::disposeComponent(xStorageProperties);
}
} catch(Exception&){}
return OUString();
}
// XServiceInfo
OUString SAL_CALL DBTypeDetection::getImplementationName()
{
return u"org.openoffice.comp.dbflt.DBTypeDetection"_ustr;
}
// XServiceInfo
sal_Bool SAL_CALL DBTypeDetection::supportsService(const OUString& ServiceName)
{
return cppu::supportsService(this, ServiceName);
}
// XServiceInfo
Sequence< OUString > SAL_CALL DBTypeDetection::getSupportedServiceNames()
{
return { u"com.sun.star.document.ExtendedTypeDetection"_ustr };
}
} // namespace dbaxml
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbflt_DBTypeDetection_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
{
return cppu::acquire(new ::dbaxml::DBTypeDetection(context));
}
namespace dbaxml
{
namespace {
class DBContentLoader : public ::cppu::WeakImplHelper< XFrameLoader, XServiceInfo>
{
private:
const Reference< XComponentContext > m_aContext;
rtl::Reference< DBContentLoader > m_xMySelf;
OUString m_sCurrentURL;
ImplSVEvent * m_nStartWizard;
DECL_LINK( OnStartTableWizard, void*, void );
public:
explicit DBContentLoader(const Reference< XComponentContext >&);
// XServiceInfo
OUString SAL_CALL getImplementationName() override;
sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
// XLoader
virtual void SAL_CALL load( const Reference< XFrame > & _rFrame, const OUString& _rURL,
const Sequence< PropertyValue >& _rArgs,
const Reference< XLoadEventListener > & _rListener) override;
virtual void SAL_CALL cancel() override;
private:
bool impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard );
};
}
DBContentLoader::DBContentLoader(const Reference< XComponentContext >& _rxFactory)
:m_aContext( _rxFactory )
,m_nStartWizard(nullptr)
{
}
// XServiceInfo
OUString SAL_CALL DBContentLoader::getImplementationName()
{
return u"org.openoffice.comp.dbflt.DBContentLoader2"_ustr;
}
// XServiceInfo
sal_Bool SAL_CALL DBContentLoader::supportsService(const OUString& ServiceName)
{
return cppu::supportsService(this, ServiceName);
}
// XServiceInfo
Sequence< OUString > SAL_CALL DBContentLoader::getSupportedServiceNames()
{
return { u"com.sun.star.frame.FrameLoader"_ustr };
}
namespace
{
bool lcl_urlAllowsInteraction( const Reference<XComponentContext> & _rContext, const OUString& _rURL )
{
bool bDoesAllow = false;
try
{
Reference< XURLTransformer > xTransformer( URLTransformer::create(_rContext) );
URL aURL;
aURL.Complete = _rURL;
xTransformer->parseStrict( aURL );
bDoesAllow = aURL.Arguments == "Interactive";
}
catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_urlAllowsInteraction: caught an exception while analyzing the URL!" );
}
return bDoesAllow;
}
Reference< XWindow > lcl_getTopMostWindow( const Reference<XComponentContext> & _rxContext )
{
Reference< XWindow > xWindow;
// get the top most window
Reference < XDesktop2 > xDesktop = Desktop::create(_rxContext);
Reference < XFrame > xActiveFrame = xDesktop->getActiveFrame();
if ( xActiveFrame.is() )
{
xWindow = xActiveFrame->getContainerWindow();
Reference<XFrame> xFrame = xActiveFrame;
while ( xFrame.is() && !xFrame->isTop() )
xFrame = xFrame->getCreator();
if ( xFrame.is() )
xWindow = xFrame->getContainerWindow();
}
return xWindow;
}
}
bool DBContentLoader::impl_executeNewDatabaseWizard( Reference< XModel > const & _rxModel, bool& _bShouldStartTableWizard )
{
Sequence<Any> aWizardArgs(comphelper::InitAnyPropertySequence(
{
{"ParentWindow", Any(lcl_getTopMostWindow( m_aContext ))},
{"InitialSelection", Any(_rxModel)}
}));
// create the dialog
Reference< XExecutableDialog > xAdminDialog( m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(u"com.sun.star.sdb.DatabaseWizardDialog"_ustr, aWizardArgs, m_aContext), UNO_QUERY_THROW);
// execute it
if ( RET_OK != xAdminDialog->execute() )
return false;
Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
bool bSuccess = false;
xProp->getPropertyValue(u"OpenDatabase"_ustr) >>= bSuccess;
xProp->getPropertyValue(u"StartTableWizard"_ustr) >>= _bShouldStartTableWizard;
return bSuccess;
}
void SAL_CALL DBContentLoader::load(const Reference< XFrame > & rFrame, const OUString& _rURL,
const Sequence< PropertyValue >& rArgs,
const Reference< XLoadEventListener > & rListener)
{
// first check if preview is true, if so return without creating a controller. Preview is not supported
::comphelper::NamedValueCollection aMediaDesc( rArgs );
bool bPreview = aMediaDesc.getOrDefault( u"Preview"_ustr, false );
if ( bPreview )
{
if (rListener.is())
rListener->loadCancelled(this);
return;
}
Reference< XModel > xModel = aMediaDesc.getOrDefault( u"Model"_ustr, Reference< XModel >() );
OUString sSalvagedURL = aMediaDesc.getOrDefault( u"SalvagedFile"_ustr, _rURL );
bool bCreateNew = false; // does the URL denote the private:factory URL?
bool bStartTableWizard = false; // start the table wizard after everything was loaded successfully?
bool bSuccess = true;
// If there's no interaction handler in the media descriptor, put one.
// By definition, loading via loadComponentFromURL (and thus via the content loader here)
// is allowed to raise UI. To not burden every place inside the document with creating
// a default handler, we simply ensure there is one.
// If a handler is present in the media descriptor, even if it is NULL, we will
// not touch it.
if ( !aMediaDesc.has( u"InteractionHandler"_ustr ) )
{
Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) );
aMediaDesc.put( u"InteractionHandler"_ustr, xHandler );
}
// it's allowed to pass an existing document
Reference< XOfficeDatabaseDocument > xExistentDBDoc;
xModel.set( aMediaDesc.getOrDefault( u"Model"_ustr, xExistentDBDoc ), UNO_QUERY );
aMediaDesc.remove( u"Model"_ustr );
// also, it's allowed to specify the type of view which should be created
OUString sViewName = aMediaDesc.getOrDefault( u"ViewName"_ustr, u"Default"_ustr );
aMediaDesc.remove( u"ViewName"_ustr );
// this needs to stay alive for duration of this method
Reference< XDatabaseContext > xDatabaseContext;
sal_Int32 nInitialSelection = -1;
if ( !xModel.is() )
{
xDatabaseContext = DatabaseContext::create(m_aContext);
OUString sFactoryName = SvtModuleOptions().GetFactoryEmptyDocumentURL(SvtModuleOptions::EFactory::DATABASE);
bCreateNew = sFactoryName.match(_rURL);
Reference< XDocumentDataSource > xDocumentDataSource;
bool bNewAndInteractive = false;
if ( bCreateNew )
{
bNewAndInteractive = lcl_urlAllowsInteraction( m_aContext, _rURL );
xDocumentDataSource.set( xDatabaseContext->createInstance(), UNO_QUERY_THROW );
}
else
{
::comphelper::NamedValueCollection aCreationArgs;
aCreationArgs.put( INFO_POOLURL, sSalvagedURL );
xDocumentDataSource.set( xDatabaseContext->createInstanceWithArguments( aCreationArgs.getWrappedNamedValues() ), UNO_QUERY_THROW );
}
xModel.set( xDocumentDataSource->getDatabaseDocument(), UNO_QUERY );
if ( bCreateNew && xModel.is() )
{
if ( bNewAndInteractive )
{
bSuccess = impl_executeNewDatabaseWizard( xModel, bStartTableWizard );
}
else
{
try
{
Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW );
xLoad->initNew();
bSuccess = true;
}
catch( const Exception& )
{
bSuccess = false;
}
}
// initially select the "Tables" category (will be done below)
nInitialSelection = css::sdb::application::DatabaseObjectContainer::TABLES;
}
}
if ( !xModel.is() )
{
if ( rListener.is() )
rListener->loadCancelled(this);
return;
}
if ( !bCreateNew )
{
// We need to XLoadable::load the document if it does not yet have a URL.
// If it already *does* have a URL, then it was either passed in the arguments, or a previous incarnation
// of that model existed before (which can happen if a model is closed, but an associated DataSource is kept
// alive 'til loading the document again).
bool bNeedLoad = xModel->getURL().isEmpty();
try
{
aMediaDesc.put( u"FileName"_ustr, _rURL );
Sequence< PropertyValue > aResource( aMediaDesc.getPropertyValues() );
if ( bNeedLoad )
{
Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW );
xLoad->load( aResource );
}
// always attach the resource, even if the document has not been freshly loaded
xModel->attachResource( _rURL, aResource );
}
catch(const Exception&)
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
bSuccess = false;
}
}
if ( bSuccess )
{
try
{
Reference< XModel2 > xModel2( xModel, UNO_QUERY_THROW );
Reference< XController2 > xController( xModel2->createViewController( sViewName, Sequence< PropertyValue >(), rFrame ), UNO_SET_THROW );
// introduce model/view/controller to each other
utl::ConnectFrameControllerModel(rFrame, xController, xModel);
bSuccess = true;
}
catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("dbaccess");
bSuccess = false;
}
}
if (bSuccess)
{
if ( rListener.is() )
rListener->loadFinished(this);
if ( nInitialSelection != -1 )
{
Reference< css::view::XSelectionSupplier > xDocView( xModel->getCurrentController(), UNO_QUERY );
if ( xDocView.is() )
{
NamedDatabaseObject aSelection;
aSelection.Type = nInitialSelection;
xDocView->select( Any( aSelection ) );
}
}
if ( bStartTableWizard )
{
// reset the data of the previous async drop (if any)
if ( m_nStartWizard )
Application::RemoveUserEvent(m_nStartWizard);
m_sCurrentURL = xModel->getURL();
m_xMySelf = this;
m_nStartWizard = Application::PostUserEvent(LINK(this, DBContentLoader, OnStartTableWizard));
}
}
else
{
if ( rListener.is() )
rListener->loadCancelled( this );
}
if ( !bSuccess )
::comphelper::disposeComponent(xModel);
}
void DBContentLoader::cancel()
{
}
IMPL_LINK_NOARG( DBContentLoader, OnStartTableWizard, void*, void )
{
m_nStartWizard = nullptr;
try
{
Sequence<Any> aWizArgs(comphelper::InitAnyPropertySequence(
{
{"DatabaseLocation", Any(m_sCurrentURL)}
}));
SolarMutexGuard aGuard;
Reference< XJobExecutor > xTableWizard( m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(u"com.sun.star.wizards.table.CallTableWizard"_ustr, aWizArgs, m_aContext), UNO_QUERY);
if ( xTableWizard.is() )
xTableWizard->trigger(u"start"_ustr);
}
catch(const Exception&)
{
TOOLS_WARN_EXCEPTION( "dbaccess", "caught an exception while starting the table wizard!");
}
m_xMySelf = nullptr;
}
}
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
org_openoffice_comp_dbflt_DBContentLoader2_get_implementation(
css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
{
return cppu::acquire(new ::dbaxml::DBContentLoader(context));
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1048 The 'bSuccess' variable was assigned the same value.
↑ V1048 The 'bSuccess' variable was assigned the same value.