From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- .../source/core/dataaccess/ComponentDefinition.cxx | 285 +++ .../source/core/dataaccess/ComponentDefinition.hxx | 159 ++ dbaccess/source/core/dataaccess/ContentHelper.cxx | 615 ++++++ dbaccess/source/core/dataaccess/ModelImpl.cxx | 1432 +++++++++++++ .../source/core/dataaccess/SharedConnection.cxx | 154 ++ .../source/core/dataaccess/SharedConnection.hxx | 118 ++ .../source/core/dataaccess/bookmarkcontainer.cxx | 301 +++ .../source/core/dataaccess/commandcontainer.cxx | 100 + .../source/core/dataaccess/commandcontainer.hxx | 72 + .../source/core/dataaccess/commanddefinition.cxx | 153 ++ .../source/core/dataaccess/commanddefinition.hxx | 125 ++ dbaccess/source/core/dataaccess/connection.cxx | 808 +++++++ dbaccess/source/core/dataaccess/connection.hxx | 229 ++ .../core/dataaccess/dataaccessdescriptor.cxx | 229 ++ .../source/core/dataaccess/databasecontext.cxx | 753 +++++++ .../source/core/dataaccess/databasedocument.cxx | 2211 ++++++++++++++++++++ .../source/core/dataaccess/databasedocument.hxx | 753 +++++++ .../core/dataaccess/databaseregistrations.cxx | 369 ++++ .../core/dataaccess/databaseregistrations.hxx | 32 + dbaccess/source/core/dataaccess/datasource.cxx | 1437 +++++++++++++ dbaccess/source/core/dataaccess/datasource.hxx | 220 ++ .../source/core/dataaccess/definitioncontainer.cxx | 679 ++++++ .../source/core/dataaccess/documentcontainer.cxx | 770 +++++++ .../source/core/dataaccess/documentcontainer.hxx | 137 ++ .../source/core/dataaccess/documentdefinition.cxx | 2102 +++++++++++++++++++ .../source/core/dataaccess/documentdefinition.hxx | 356 ++++ .../core/dataaccess/documenteventexecutor.cxx | 199 ++ .../core/dataaccess/documenteventexecutor.hxx | 59 + .../core/dataaccess/documenteventnotifier.cxx | 290 +++ .../core/dataaccess/documenteventnotifier.hxx | 128 ++ dbaccess/source/core/dataaccess/documentevents.cxx | 216 ++ dbaccess/source/core/dataaccess/intercept.cxx | 363 ++++ dbaccess/source/core/dataaccess/intercept.hxx | 113 + .../source/core/dataaccess/myucp_datasupplier.cxx | 330 +++ .../source/core/dataaccess/myucp_datasupplier.hxx | 59 + .../source/core/dataaccess/myucp_resultset.cxx | 74 + .../source/core/dataaccess/myucp_resultset.hxx | 49 + 37 files changed, 16479 insertions(+) create mode 100644 dbaccess/source/core/dataaccess/ComponentDefinition.cxx create mode 100644 dbaccess/source/core/dataaccess/ComponentDefinition.hxx create mode 100644 dbaccess/source/core/dataaccess/ContentHelper.cxx create mode 100644 dbaccess/source/core/dataaccess/ModelImpl.cxx create mode 100644 dbaccess/source/core/dataaccess/SharedConnection.cxx create mode 100644 dbaccess/source/core/dataaccess/SharedConnection.hxx create mode 100644 dbaccess/source/core/dataaccess/bookmarkcontainer.cxx create mode 100644 dbaccess/source/core/dataaccess/commandcontainer.cxx create mode 100644 dbaccess/source/core/dataaccess/commandcontainer.hxx create mode 100644 dbaccess/source/core/dataaccess/commanddefinition.cxx create mode 100644 dbaccess/source/core/dataaccess/commanddefinition.hxx create mode 100644 dbaccess/source/core/dataaccess/connection.cxx create mode 100644 dbaccess/source/core/dataaccess/connection.hxx create mode 100644 dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx create mode 100644 dbaccess/source/core/dataaccess/databasecontext.cxx create mode 100644 dbaccess/source/core/dataaccess/databasedocument.cxx create mode 100644 dbaccess/source/core/dataaccess/databasedocument.hxx create mode 100644 dbaccess/source/core/dataaccess/databaseregistrations.cxx create mode 100644 dbaccess/source/core/dataaccess/databaseregistrations.hxx create mode 100644 dbaccess/source/core/dataaccess/datasource.cxx create mode 100644 dbaccess/source/core/dataaccess/datasource.hxx create mode 100644 dbaccess/source/core/dataaccess/definitioncontainer.cxx create mode 100644 dbaccess/source/core/dataaccess/documentcontainer.cxx create mode 100644 dbaccess/source/core/dataaccess/documentcontainer.hxx create mode 100644 dbaccess/source/core/dataaccess/documentdefinition.cxx create mode 100644 dbaccess/source/core/dataaccess/documentdefinition.hxx create mode 100644 dbaccess/source/core/dataaccess/documenteventexecutor.cxx create mode 100644 dbaccess/source/core/dataaccess/documenteventexecutor.hxx create mode 100644 dbaccess/source/core/dataaccess/documenteventnotifier.cxx create mode 100644 dbaccess/source/core/dataaccess/documenteventnotifier.hxx create mode 100644 dbaccess/source/core/dataaccess/documentevents.cxx create mode 100644 dbaccess/source/core/dataaccess/intercept.cxx create mode 100644 dbaccess/source/core/dataaccess/intercept.hxx create mode 100644 dbaccess/source/core/dataaccess/myucp_datasupplier.cxx create mode 100644 dbaccess/source/core/dataaccess/myucp_datasupplier.hxx create mode 100644 dbaccess/source/core/dataaccess/myucp_resultset.cxx create mode 100644 dbaccess/source/core/dataaccess/myucp_resultset.hxx (limited to 'dbaccess/source/core/dataaccess') diff --git a/dbaccess/source/core/dataaccess/ComponentDefinition.cxx b/dbaccess/source/core/dataaccess/ComponentDefinition.cxx new file mode 100644 index 000000000..a3a645982 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ComponentDefinition.cxx @@ -0,0 +1,285 @@ +/* -*- 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 "ComponentDefinition.hxx" +#include +#include + +#include +#include +//#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::cppu; + +namespace dbaccess +{ + +/// helper class for column property change events which holds the OComponentDefinition weak +class OColumnPropertyListener: + public ::cppu::WeakImplHelper< XPropertyChangeListener > +{ + OComponentDefinition* m_pComponent; +protected: + virtual ~OColumnPropertyListener() override {} +public: + explicit OColumnPropertyListener(OComponentDefinition* _pComponent) : m_pComponent(_pComponent){} + OColumnPropertyListener(const OColumnPropertyListener&) = delete; + const OColumnPropertyListener& operator=(const OColumnPropertyListener&) = delete; + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const PropertyChangeEvent& /*_rEvent*/ ) override + { + if ( m_pComponent ) + m_pComponent->notifyDataSourceModified(); + } + // XEventListener + virtual void SAL_CALL disposing( const EventObject& /*_rSource*/ ) override + { + } + void clear() { m_pComponent = nullptr; } +}; + +OComponentDefinition_Impl::OComponentDefinition_Impl() +{ +} + +OComponentDefinition_Impl::~OComponentDefinition_Impl() +{ +} + +// OComponentDefinition + + +void OComponentDefinition::initialize( const Sequence< Any >& aArguments ) +{ + OUString rName; + if( (aArguments.getLength() == 1) && (aArguments[0] >>= rName) ) + { + Sequence aNewArgs(comphelper::InitAnyPropertySequence( + { + {PROPERTY_NAME, Any(rName)} + })); + OContentHelper::initialize(aNewArgs); + } + else + OContentHelper::initialize(aArguments); +} + +void OComponentDefinition::registerProperties() +{ + m_xColumnPropertyListener = new OColumnPropertyListener(this); + OComponentDefinition_Impl& rDefinition( getDefinition() ); + ODataSettings::registerPropertiesFor( &rDefinition ); + + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND | PropertyAttribute::READONLY|PropertyAttribute::CONSTRAINED, + &rDefinition.m_aProps.aTitle, cppu::UnoType::get()); + + if ( m_bTable ) + { + registerProperty(PROPERTY_SCHEMANAME, PROPERTY_ID_SCHEMANAME, PropertyAttribute::BOUND, + &rDefinition.m_sSchemaName, cppu::UnoType::get()); + + registerProperty(PROPERTY_CATALOGNAME, PROPERTY_ID_CATALOGNAME, PropertyAttribute::BOUND, + &rDefinition.m_sCatalogName, cppu::UnoType::get()); + } +} + +OComponentDefinition::OComponentDefinition(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTable) + :OContentHelper(_xORB,_xParentContainer,_pImpl) + ,ODataSettings(OContentHelper::rBHelper,!_bTable) + ,m_bTable(_bTable) +{ + registerProperties(); +} + +OComponentDefinition::~OComponentDefinition() +{ +} + +OComponentDefinition::OComponentDefinition( const Reference< XInterface >& _rxContainer + ,const OUString& _rElementName + ,const Reference< XComponentContext >& _xORB + ,const TContentPtr& _pImpl + ,bool _bTable) + :OContentHelper(_xORB,_rxContainer,_pImpl) + ,ODataSettings(OContentHelper::rBHelper,!_bTable) + ,m_bTable(_bTable) +{ + registerProperties(); + + m_pImpl->m_aProps.aTitle = _rElementName; + OSL_ENSURE(!m_pImpl->m_aProps.aTitle.isEmpty(), "OComponentDefinition::OComponentDefinition : invalid name !"); +} + +css::uno::Sequence OComponentDefinition::getImplementationId() +{ + return css::uno::Sequence(); +} + +css::uno::Sequence< css::uno::Type > OComponentDefinition::getTypes() +{ + return ::comphelper::concatSequences( + ODataSettings::getTypes( ), + OContentHelper::getTypes( ), + OComponentDefinition_BASE::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE3( OComponentDefinition,OContentHelper,ODataSettings,OComponentDefinition_BASE) + +OUString SAL_CALL OComponentDefinition::getImplementationName() +{ + return "com.sun.star.comp.dba.OComponentDefinition"; +} + +Sequence< OUString > SAL_CALL OComponentDefinition::getSupportedServiceNames() +{ + return { "com.sun.star.sdb.TableDefinition", "com.sun.star.ucb.Content" }; +} + +void SAL_CALL OComponentDefinition::disposing() +{ + OContentHelper::disposing(); + if (m_pColumns) + { + m_pColumns->disposing(); + } + m_xColumnPropertyListener->clear(); + m_xColumnPropertyListener.clear(); +} + +IPropertyArrayHelper& OComponentDefinition::getInfoHelper() +{ + return *getArrayHelper(); +} + +IPropertyArrayHelper* OComponentDefinition::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties(aProps); + return new OPropertyArrayHelper(aProps); +} + +Reference< XPropertySetInfo > SAL_CALL OComponentDefinition::getPropertySetInfo( ) +{ + Reference xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +OUString OComponentDefinition::determineContentType() const +{ + return m_bTable + ? OUString( "application/vnd.org.openoffice.DatabaseTable" ) + : OUString( "application/vnd.org.openoffice.DatabaseCommandDefinition" ); +} + +Reference< XNameAccess> OComponentDefinition::getColumns() +{ + ::osl::MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(OContentHelper::rBHelper.bDisposed); + + if (!m_pColumns) + { + std::vector< OUString> aNames; + + const OComponentDefinition_Impl& rDefinition( getDefinition() ); + aNames.reserve( rDefinition.size() ); + + for (auto const& definition : rDefinition) + aNames.push_back(definition.first); + + m_pColumns.reset(new OColumns(*this, m_aMutex, true, aNames, this, nullptr, true, false, false)); + m_pColumns->setParent(*this); + } + // see OCollection::acquire + return m_pColumns.get(); +} + +rtl::Reference OComponentDefinition::createColumn(const OUString& _rName) const +{ + const OComponentDefinition_Impl& rDefinition( getDefinition() ); + OComponentDefinition_Impl::const_iterator aFind = rDefinition.find( _rName ); + if ( aFind != rDefinition.end() ) + { + aFind->second->addPropertyChangeListener(OUString(),m_xColumnPropertyListener); + return new OTableColumnWrapper( aFind->second, aFind->second, true ); + } + OSL_FAIL( "OComponentDefinition::createColumn: is this a valid case?" ); + // This here is the last place creating a OTableColumn, and somehow /me thinks it is not needed ... + return new OTableColumn( _rName ); +} + +Reference< XPropertySet > OComponentDefinition::createColumnDescriptor() +{ + return new OTableColumnDescriptor( true ); +} + +void OComponentDefinition::setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any& rValue) +{ + ODataSettings::setFastPropertyValue_NoBroadcast(nHandle,rValue); + notifyDataSourceModified(); +} + +void OComponentDefinition::columnDropped(const OUString& _sName) +{ + getDefinition().erase( _sName ); + notifyDataSourceModified(); +} + +void OComponentDefinition::columnAppended( const Reference< XPropertySet >& _rxSourceDescriptor ) +{ + OUString sName; + _rxSourceDescriptor->getPropertyValue( PROPERTY_NAME ) >>= sName; + + Reference xColDesc = new OTableColumnDescriptor( true ); + ::comphelper::copyProperties( _rxSourceDescriptor, xColDesc ); + getDefinition().insert( sName, xColDesc ); + + // formerly, here was a setParent at the xColDesc. The parent used was an adapter (ChildHelper_Impl) + // which held another XChild weak, and forwarded all getParent requests to this other XChild. + // m_pColumns was used for this. This was nonsense, since m_pColumns dies when our instance dies, + // but xColDesc will live longer than this. So effectively, the setParent call was pretty useless. + // + // The intention for this parenting was that the column descriptor is able to find the data source, + // by traveling up the parent hierarchy until there's an XDataSource. This didn't work (which + // for instance causes #i65023#). We need another way to properly ensure this. + + notifyDataSourceModified(); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_OComponentDefinition(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new dbaccess::OComponentDefinition( + context, nullptr, std::make_shared())); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/ComponentDefinition.hxx b/dbaccess/source/core/dataaccess/ComponentDefinition.hxx new file mode 100644 index 000000000..4c10a5354 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ComponentDefinition.hxx @@ -0,0 +1,159 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +namespace dbaccess +{ + + typedef ::cppu::ImplHelper1< css::sdbcx::XColumnsSupplier > OComponentDefinition_BASE; + + class OComponentDefinition_Impl : public OContentHelper_Impl + ,public ODataSettings_Base + { + public: + typedef std::map < OUString + , css::uno::Reference< css::beans::XPropertySet > + > Columns; + typedef Columns::iterator iterator; + typedef Columns::const_iterator const_iterator; + + private: + Columns m_aColumns; + + public: + OUString m_sSchemaName; + OUString m_sCatalogName; + + public: + OComponentDefinition_Impl(); + virtual ~OComponentDefinition_Impl() override; + + size_t size() const { return m_aColumns.size(); } + + const_iterator begin() const { return m_aColumns.begin(); } + const_iterator end() const { return m_aColumns.end(); } + + const_iterator find( const OUString& _rName ) const { return m_aColumns.find( _rName ); } + + void erase( const OUString& _rName ) { m_aColumns.erase( _rName ); } + + void insert( const OUString& _rName, const css::uno::Reference< css::beans::XPropertySet >& _rxColumn ) + { + OSL_PRECOND( m_aColumns.find( _rName ) == m_aColumns.end(), "OComponentDefinition_Impl::insert: there's already an element with this name!" ); + m_aColumns.emplace( _rName, _rxColumn ); + } + }; + +class OColumnPropertyListener; +// OComponentDefinition - a database "document" which describes a query +class OComponentDefinition :public OContentHelper + ,public ODataSettings + ,public IColumnFactory + ,public OComponentDefinition_BASE + ,public ::comphelper::OPropertyArrayUsageHelper< OComponentDefinition > +{ + // no Reference! see OCollection::acquire + std::unique_ptr m_pColumns; + rtl::Reference m_xColumnPropertyListener; + bool m_bTable; + +protected: + virtual ~OComponentDefinition() override; + virtual void SAL_CALL disposing() override; + + const OComponentDefinition_Impl& getDefinition() const { return dynamic_cast< const OComponentDefinition_Impl& >( *m_pImpl ); } + OComponentDefinition_Impl& getDefinition() { return dynamic_cast< OComponentDefinition_Impl& >( *m_pImpl ); } +public: + OComponentDefinition( + const css::uno::Reference< css::uno::XComponentContext >&, + const css::uno::Reference< css::uno::XInterface >& _xParentContainer, + const TContentPtr& _pImpl, + bool _bTable = true); + + OComponentDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer + ,const OUString& _rElementName + ,const css::uno::Reference< css::uno::XComponentContext >& + ,const TContentPtr& _pImpl + ,bool _bTable = true + ); + + virtual css::uno::Sequence SAL_CALL getTypes() override; + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + + // css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( css::uno::Sequence< css::uno::Any > const & rArguments) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XColumnsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getColumns( ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // IColumnFactory + virtual rtl::Reference createColumn(const OUString& _rName) const override; + virtual css::uno::Reference< css::beans::XPropertySet > createColumnDescriptor() override; + virtual void columnAppended( const css::uno::Reference< css::beans::XPropertySet >& _rxSourceDescriptor ) override; + virtual void columnDropped(const OUString& _sName) override; + using OContentHelper::notifyDataSourceModified; + +protected: +// OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + // OContentHelper overridables + virtual OUString determineContentType() const override; + +private: + void registerProperties(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/ContentHelper.cxx b/dbaccess/source/core/dataaccess/ContentHelper.cxx new file mode 100644 index 000000000..558dd7450 --- /dev/null +++ b/dbaccess/source/core/dataaccess/ContentHelper.cxx @@ -0,0 +1,615 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::cppu; + +OContentHelper_Impl::OContentHelper_Impl() + : m_pDataSource(nullptr) +{ +} + +OContentHelper_Impl::~OContentHelper_Impl() +{ +} + +OContentHelper::OContentHelper(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl) + : OContentHelper_COMPBASE(m_aMutex) + ,m_aContentListeners(m_aMutex) + ,m_aPropertyChangeListeners(m_aMutex) + ,m_xParentContainer( _xParentContainer ) + ,m_aContext( _xORB ) + ,m_pImpl(_pImpl) + ,m_nCommandId(0) +{ +} + +void SAL_CALL OContentHelper::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + // say goodbye to our listeners + EventObject aEvt(*this); + m_aContentListeners.disposeAndClear(aEvt); + + m_xParentContainer = nullptr; +} + +OUString SAL_CALL OContentHelper::getImplementationName() + { + return "com.sun.star.comp.sdb.Content"; + } +sal_Bool SAL_CALL OContentHelper::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + } +css::uno::Sequence< OUString > SAL_CALL OContentHelper::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.Content" }; +} + + +const css::uno::Sequence & OContentHelper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit aId; + return aId.getSeq(); +} + +css::uno::Sequence OContentHelper::getImplementationId() +{ + return css::uno::Sequence(); +} + +// XContent +Reference< XContentIdentifier > SAL_CALL OContentHelper::getIdentifier( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + OUString aIdentifier( "private:" + impl_getHierarchicalName( true ) ); + return new ::ucbhelper::ContentIdentifier( aIdentifier ); +} + +OUString OContentHelper::impl_getHierarchicalName( bool _includingRootContainer ) const +{ + OUStringBuffer aHierarchicalName( m_pImpl->m_aProps.aTitle ); + Reference< XInterface > xParent = m_xParentContainer; + while( xParent.is() ) + { + Reference xProp( xParent, UNO_QUERY ); + Reference< XChild > xChild( xParent, UNO_QUERY ); + xParent.set( xChild.is() ? xChild->getParent() : Reference< XInterface >(), UNO_QUERY ); + if ( xProp.is() && xParent.is() ) + { + OUString sName; + xProp->getPropertyValue( PROPERTY_NAME ) >>= sName; + + OUString sPrevious = aHierarchicalName.makeStringAndClear(); + aHierarchicalName.append( sName + "/" + sPrevious ); + } + } + OUString sHierarchicalName( aHierarchicalName.makeStringAndClear() ); + if ( !_includingRootContainer ) + sHierarchicalName = sHierarchicalName.copy( sHierarchicalName.indexOf( '/' ) + 1 ); + return sHierarchicalName; +} + +OUString SAL_CALL OContentHelper::getContentType() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if ( !m_pImpl->m_aProps.aContentType ) + { // content type not yet retrieved + m_pImpl->m_aProps.aContentType = determineContentType(); + } + + return *m_pImpl->m_aProps.aContentType; +} + +void SAL_CALL OContentHelper::addContentEventListener( const Reference< XContentEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( _rxListener.is() ) + m_aContentListeners.addInterface(_rxListener); +} + +void SAL_CALL OContentHelper::removeContentEventListener( const Reference< XContentEventListener >& _rxListener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (_rxListener.is()) + m_aContentListeners.removeInterface(_rxListener); +} + +// XCommandProcessor +sal_Int32 SAL_CALL OContentHelper::createCommandIdentifier( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + // Just increase counter on every call to generate an identifier. + return ++m_nCommandId; +} + +Any SAL_CALL OContentHelper::execute( const Command& aCommand, sal_Int32 /*CommandId*/, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + if ( aCommand.Name == "getPropertyValues" ) + { + // getPropertyValues + + Sequence< Property > Properties; + if ( !( aCommand.Argument >>= Properties ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + aRet <<= getPropertyValues( Properties); + } + else if ( aCommand.Name == "setPropertyValues" ) + { + // setPropertyValues + + Sequence< PropertyValue > aProperties; + if ( !( aCommand.Argument >>= aProperties ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + if ( !aProperties.hasElements() ) + { + OSL_FAIL( "No properties!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + aRet <<= setPropertyValues( aProperties ); + } + else if ( aCommand.Name == "getPropertySetInfo" ) + { + // getPropertySetInfo + + Reference xProp(*this,UNO_QUERY); + if ( xProp.is() ) + aRet <<= xProp->getPropertySetInfo(); + // aRet <<= getPropertySetInfo(); // TODO + } + else + { + // Unsupported command + + OSL_FAIL( "Content::execute - unsupported command!" ); + + ucbhelper::cancelCommandExecution( + Any( UnsupportedCommandException( + OUString(), + static_cast< cppu::OWeakObject * >( this ) ) ), + Environment ); + // Unreachable + } + + return aRet; +} + +void SAL_CALL OContentHelper::abort( sal_Int32 /*CommandId*/ ) +{ +} + +// XPropertiesChangeNotifier +void SAL_CALL OContentHelper::addPropertiesChangeListener( const Sequence< OUString >& PropertyNames, const Reference< XPropertiesChangeListener >& Listener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + sal_Int32 nCount = PropertyNames.getLength(); + if ( !nCount ) + { + // Note: An empty sequence means a listener for "all" properties. + m_aPropertyChangeListeners.addInterface(OUString(), Listener ); + } + else + { + const OUString* pSeq = PropertyNames.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const OUString& rName = pSeq[ n ]; + if ( !rName.isEmpty() ) + m_aPropertyChangeListeners.addInterface(rName, Listener ); + } + } +} + +void SAL_CALL OContentHelper::removePropertiesChangeListener( const Sequence< OUString >& PropertyNames, const Reference< XPropertiesChangeListener >& Listener ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + sal_Int32 nCount = PropertyNames.getLength(); + if ( !nCount ) + { + // Note: An empty sequence means a listener for "all" properties. + m_aPropertyChangeListeners.removeInterface( OUString(), Listener ); + } + else + { + const OUString* pSeq = PropertyNames.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const OUString& rName = pSeq[ n ]; + if ( !rName.isEmpty() ) + m_aPropertyChangeListeners.removeInterface( rName, Listener ); + } + } +} + +// XPropertyContainer +void SAL_CALL OContentHelper::addProperty( const OUString& /*Name*/, sal_Int16 /*Attributes*/, const Any& /*DefaultValue*/ ) +{ + OSL_FAIL( "OContentHelper::addProperty: not implemented!" ); +} + +void SAL_CALL OContentHelper::removeProperty( const OUString& /*Name*/ ) +{ + OSL_FAIL( "OContentHelper::removeProperty: not implemented!" ); +} + +// XInitialization +void SAL_CALL OContentHelper::initialize( const Sequence< Any >& _aArguments ) +{ + const Any* pBegin = _aArguments.getConstArray(); + const Any* pEnd = pBegin + _aArguments.getLength(); + PropertyValue aValue; + for(;pBegin != pEnd;++pBegin) + { + *pBegin >>= aValue; + if ( aValue.Name == "Parent" ) + { + m_xParentContainer.set(aValue.Value,UNO_QUERY); + } + else if ( aValue.Name == PROPERTY_NAME ) + { + aValue.Value >>= m_pImpl->m_aProps.aTitle; + } + else if ( aValue.Name == PROPERTY_PERSISTENT_NAME ) + { + aValue.Value >>= m_pImpl->m_aProps.sPersistentName; + } + } +} + +Sequence< Any > OContentHelper::setPropertyValues(const Sequence< PropertyValue >& rValues ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + Sequence< Any > aRet( rValues.getLength() ); + auto aRetRange = asNonConstRange(aRet); + Sequence< PropertyChangeEvent > aChanges( rValues.getLength() ); + sal_Int32 nChanged = 0; + + PropertyChangeEvent aEvent; + aEvent.Source = static_cast< cppu::OWeakObject * >( this ); + aEvent.Further = false; + aEvent.PropertyHandle = -1; + + const PropertyValue* pValues = rValues.getConstArray(); + sal_Int32 nCount = rValues.getLength(); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const PropertyValue& rValue = pValues[ n ]; + + if ( rValue.Name == "ContentType" || rValue.Name == "IsDocument" || rValue.Name == "IsFolder" ) + { + // Read-only property! + aRetRange[ n ] <<= IllegalAccessException("Property is read-only!", + static_cast< cppu::OWeakObject * >( this ) ); + } + else if ( rValue.Name == "Title" ) + { + OUString aNewValue; + if ( rValue.Value >>= aNewValue ) + { + if ( aNewValue != m_pImpl->m_aProps.aTitle ) + { + aEvent.PropertyName = rValue.Name; + aEvent.OldValue <<= m_pImpl->m_aProps.aTitle; + + try + { + impl_rename_throw( aNewValue ,false); + OSL_ENSURE( m_pImpl->m_aProps.aTitle == aNewValue, "OContentHelper::setPropertyValues('Title'): rename did not work!" ); + + aEvent.NewValue <<= aNewValue; + aChanges.getArray()[ nChanged ] = aEvent; + nChanged++; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "OContentHelper::setPropertyValues('Title'): caught an exception while renaming!" ); + } + } + else + { + // Old value equals new value. No error! + } + } + else + { + aRetRange[ n ] <<= IllegalTypeException("Property value has wrong type!", + static_cast< cppu::OWeakObject * >( this ) ); + } + } + + else + { + aRetRange[ n ] <<= Exception("No property set for storing the value!", + static_cast< cppu::OWeakObject * >( this ) ); + } + } + + if ( nChanged > 0 ) + { + notifyDataSourceModified(); + aGuard.clear(); + aChanges.realloc( nChanged ); + notifyPropertiesChange( aChanges ); + } + + return aRet; +} + +// static +Reference< XRow > OContentHelper::getPropertyValues( const Sequence< Property >& rProperties) +{ + // Note: Empty sequence means "get values of all supported properties". + + rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_aContext ); + + sal_Int32 nCount = rProperties.getLength(); + if ( nCount ) + { + const Property* pProps = rProperties.getConstArray(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const Property& rProp = pProps[ n ]; + + // Process Core properties. + + if ( rProp.Name == "ContentType" ) + { + xRow->appendString ( rProp, getContentType() ); + } + else if ( rProp.Name == "Title" ) + { + xRow->appendString ( rProp, m_pImpl->m_aProps.aTitle ); + } + else if ( rProp.Name == "IsDocument" ) + { + xRow->appendBoolean( rProp, m_pImpl->m_aProps.bIsDocument ); + } + else if ( rProp.Name == "IsFolder" ) + { + xRow->appendBoolean( rProp, m_pImpl->m_aProps.bIsFolder ); + } + else + xRow->appendVoid(rProp); + } + } + else + { + // Append all Core Properties. + xRow->appendString ( + Property( "ContentType", -1, + cppu::UnoType::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + getContentType() ); + xRow->appendString ( + Property( "Title", -1, + cppu::UnoType::get(), + PropertyAttribute::BOUND ), + m_pImpl->m_aProps.aTitle ); + xRow->appendBoolean( + Property( "IsDocument", -1, + cppu::UnoType::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + m_pImpl->m_aProps.bIsDocument ); + xRow->appendBoolean( + Property( "IsFolder", -1, + cppu::UnoType::get(), + PropertyAttribute::BOUND + | PropertyAttribute::READONLY ), + m_pImpl->m_aProps.bIsFolder ); + + // @@@ Append other properties supported directly. + } + + return xRow; +} + +void OContentHelper::notifyPropertiesChange( const Sequence< PropertyChangeEvent >& evt ) const +{ + + sal_Int32 nCount = evt.getLength(); + if ( !nCount ) + return; + + // First, notify listeners interested in changes of every property. + comphelper::OInterfaceContainerHelper3* pAllPropsContainer = m_aPropertyChangeListeners.getContainer( OUString() ); + if ( pAllPropsContainer ) + pAllPropsContainer->notifyEach( &XPropertiesChangeListener::propertiesChange, evt ); + + typedef std::map< XPropertiesChangeListener*, Sequence< PropertyChangeEvent > > PropertiesEventListenerMap; + PropertiesEventListenerMap aListeners; + + const PropertyChangeEvent* propertyChangeEvent = evt.getConstArray(); + + for ( sal_Int32 n = 0; n < nCount; ++n, ++propertyChangeEvent ) + { + const PropertyChangeEvent& rEvent = *propertyChangeEvent; + const OUString& rName = rEvent.PropertyName; + + comphelper::OInterfaceContainerHelper3* pPropsContainer = m_aPropertyChangeListeners.getContainer( rName ); + if ( pPropsContainer ) + { + comphelper::OInterfaceIteratorHelper3 aIter( *pPropsContainer ); + while ( aIter.hasMoreElements() ) + { + Sequence< PropertyChangeEvent >* propertyEvents; + + XPropertiesChangeListener* pListener = aIter.next().get(); + PropertiesEventListenerMap::iterator it = aListeners.find( pListener ); + if ( it == aListeners.end() ) + { + // Not in map - create and insert new entry. + auto pair = aListeners.emplace( pListener, Sequence< PropertyChangeEvent >( nCount )); + propertyEvents = &pair.first->second; + } + else + propertyEvents = &(*it).second; + + propertyEvents->getArray()[n] = rEvent; + } + } + } + + // Notify listeners. + for (auto & rPair : aListeners) + { + XPropertiesChangeListener* pListener = rPair.first; + Sequence< PropertyChangeEvent >& rSeq = rPair.second; + + // Propagate event. + pListener->propertiesChange( rSeq ); + } +} + +// css::lang::XUnoTunnel +sal_Int64 OContentHelper::getSomething( const Sequence< sal_Int8 > & rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +Reference< XInterface > SAL_CALL OContentHelper::getParent( ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + return m_xParentContainer; +} + +void SAL_CALL OContentHelper::setParent( const Reference< XInterface >& _xParent ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + m_xParentContainer = _xParent; +} + +void OContentHelper::impl_rename_throw(const OUString& _sNewName,bool _bNotify ) +{ + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + if ( _sNewName == m_pImpl->m_aProps.aTitle ) + return; + try + { + Sequence aChanges{ + { /* Source */ static_cast(this), + /* PropertyName */ PROPERTY_NAME, + /* Further */ false, + /* PropertyHandle */ PROPERTY_ID_NAME, + /* OldValue */ Any(m_pImpl->m_aProps.aTitle), + /* NewValue */ Any(_sNewName) } + }; + + aGuard.clear(); + + m_pImpl->m_aProps.aTitle = _sNewName; + if ( _bNotify ) + notifyPropertiesChange( aChanges ); + notifyDataSourceModified(); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(_sNewName,*this); + } +} + +void SAL_CALL OContentHelper::rename( const OUString& newName ) +{ + + impl_rename_throw(newName); + +} + +void OContentHelper::notifyDataSourceModified() +{ + ::dbaccess::notifyDataSourceModified(m_xParentContainer); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/ModelImpl.cxx b/dbaccess/source/core/dataaccess/ModelImpl.cxx new file mode 100644 index 000000000..5a4078fcc --- /dev/null +++ b/dbaccess/source/core/dataaccess/ModelImpl.cxx @@ -0,0 +1,1432 @@ +/* -*- 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 +#include "databasedocument.hxx" +#include "datasource.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace css; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::script; +using namespace ::cppu; +using namespace ::osl; +using namespace ::comphelper; + +namespace dbaccess +{ + +// DocumentStorageAccess +class DocumentStorageAccess : public ::cppu::WeakImplHelper< XDocumentSubStorageSupplier + , XTransactionListener > +{ + typedef std::map< OUString, Reference< XStorage > > NamedStorages; + + ::osl::Mutex m_aMutex; + /// all sub storages which we ever gave to the outer world + NamedStorages m_aExposedStorages; + ODatabaseModelImpl* m_pModelImplementation; + bool m_bPropagateCommitToRoot; + bool m_bDisposingSubStorages; + +public: + explicit DocumentStorageAccess( ODatabaseModelImpl& _rModelImplementation ) + :m_pModelImplementation( &_rModelImplementation ) + ,m_bPropagateCommitToRoot( true ) + ,m_bDisposingSubStorages( false ) + { + } + +protected: + virtual ~DocumentStorageAccess() override + { + } + +public: + void dispose(); + + // XDocumentSubStorageSupplier + virtual Reference< XStorage > SAL_CALL getDocumentSubStorage( const OUString& aStorageName, ::sal_Int32 _nMode ) override; + virtual Sequence< OUString > SAL_CALL getDocumentSubStoragesNames( ) override; + + // XTransactionListener + virtual void SAL_CALL preCommit( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL commited( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL preRevert( const css::lang::EventObject& aEvent ) override; + virtual void SAL_CALL reverted( const css::lang::EventObject& aEvent ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + /// disposes all storages managed by this instance + void disposeStorages(); + + /// disposes all known sub storages + void commitStorages(); + + /// commits the dedicated "database" storage + bool commitEmbeddedStorage( bool _bPreventRootCommits ); + +private: + /** opens the sub storage with the given name, in the given mode + */ + Reference< XStorage > impl_openSubStorage_nothrow( const OUString& _rStorageName, sal_Int32 _nMode ); + + void impl_suspendCommitPropagation() + { + OSL_ENSURE( m_bPropagateCommitToRoot, "DocumentStorageAccess::impl_suspendCommitPropagation: already suspended" ); + m_bPropagateCommitToRoot = false; + } + void impl_resumeCommitPropagation() + { + OSL_ENSURE( !m_bPropagateCommitToRoot, "DocumentStorageAccess::impl_resumeCommitPropagation: not suspended" ); + m_bPropagateCommitToRoot = true; + } + +}; + +void DocumentStorageAccess::dispose() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + for (auto const& exposedStorage : m_aExposedStorages) + { + try + { + Reference< XTransactionBroadcaster > xBroadcaster(exposedStorage.second, UNO_QUERY); + if ( xBroadcaster.is() ) + xBroadcaster->removeTransactionListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + m_aExposedStorages.clear(); + + m_pModelImplementation = nullptr; +} + +Reference< XStorage > DocumentStorageAccess::impl_openSubStorage_nothrow( const OUString& _rStorageName, sal_Int32 _nDesiredMode ) +{ + OSL_ENSURE( !_rStorageName.isEmpty(),"ODatabaseModelImpl::impl_openSubStorage_nothrow: Invalid storage name!" ); + + Reference< XStorage > xStorage; + try + { + Reference< XStorage > xRootStorage( m_pModelImplementation->getOrCreateRootStorage() ); + if ( xRootStorage.is() ) + { + sal_Int32 nRealMode = m_pModelImplementation->m_bDocumentReadOnly ? ElementModes::READ : _nDesiredMode; + if ( nRealMode == ElementModes::READ ) + { + if ( xRootStorage.is() && !xRootStorage->hasByName( _rStorageName ) ) + return xStorage; + } + + xStorage = xRootStorage->openStorageElement( _rStorageName, nRealMode ); + + Reference< XTransactionBroadcaster > xBroad( xStorage, UNO_QUERY ); + if ( xBroad.is() ) + xBroad->addTransactionListener( this ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return xStorage; +} + +void DocumentStorageAccess::disposeStorages() +{ + m_bDisposingSubStorages = true; + + for (auto & exposedStorage : m_aExposedStorages) + { + try + { + ::comphelper::disposeComponent( exposedStorage.second ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + m_aExposedStorages.clear(); + + m_bDisposingSubStorages = false; +} + +void DocumentStorageAccess::commitStorages() +{ + try + { + for (auto const& exposedStorage : m_aExposedStorages) + { + tools::stor::commitStorageIfWriteable( exposedStorage.second ); + } + } + catch(const WrappedTargetException&) + { + // WrappedTargetException not allowed to leave + throw IOException(); + } +} + +bool DocumentStorageAccess::commitEmbeddedStorage( bool _bPreventRootCommits ) +{ + if ( _bPreventRootCommits ) + impl_suspendCommitPropagation(); + + bool bSuccess = false; + try + { + NamedStorages::const_iterator pos = m_aExposedStorages.find( "database" ); + if ( pos != m_aExposedStorages.end() ) + bSuccess = tools::stor::commitStorageIfWriteable( pos->second ); + } + catch( Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( _bPreventRootCommits ) + impl_resumeCommitPropagation(); + + return bSuccess; + +} + +Reference< XStorage > SAL_CALL DocumentStorageAccess::getDocumentSubStorage( const OUString& aStorageName, ::sal_Int32 _nDesiredMode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + NamedStorages::const_iterator pos = m_aExposedStorages.find( aStorageName ); + if ( pos == m_aExposedStorages.end() ) + { + Reference< XStorage > xResult = impl_openSubStorage_nothrow( aStorageName, _nDesiredMode ); + pos = m_aExposedStorages.emplace( aStorageName, xResult ).first; + } + + return pos->second; +} + +Sequence< OUString > SAL_CALL DocumentStorageAccess::getDocumentSubStoragesNames( ) +{ + Reference< XStorage > xRootStor( m_pModelImplementation->getRootStorage() ); + if ( !xRootStor.is() ) + return Sequence< OUString >(); + + std::vector< OUString > aNames; + + const Sequence< OUString > aElementNames( xRootStor->getElementNames() ); + for ( OUString const & name : aElementNames ) + { + if ( xRootStor->isStorageElement( name ) ) + aNames.push_back( name ); + } + return aNames.empty() + ? Sequence< OUString >() + : Sequence< OUString >( aNames.data(), aNames.size() ); +} + +void SAL_CALL DocumentStorageAccess::preCommit( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::commited( const css::lang::EventObject& aEvent ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pModelImplementation ) + m_pModelImplementation->setModified( true ); + + if ( !(m_pModelImplementation && m_bPropagateCommitToRoot) ) + return; + + Reference< XStorage > xStorage( aEvent.Source, UNO_QUERY ); + + // check if this is the dedicated "database" sub storage + NamedStorages::const_iterator pos = m_aExposedStorages.find( "database" ); + if ( ( pos != m_aExposedStorages.end() ) + && ( pos->second == xStorage ) + ) + { + // if so, also commit the root storage + m_pModelImplementation->commitRootStorage(); + } +} + +void SAL_CALL DocumentStorageAccess::preRevert( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::reverted( const css::lang::EventObject& /*aEvent*/ ) +{ + // not interested in +} + +void SAL_CALL DocumentStorageAccess::disposing( const css::lang::EventObject& Source ) +{ + OSL_ENSURE( Reference< XStorage >( Source.Source, UNO_QUERY ).is(), "DocumentStorageAccess::disposing: No storage? What's this?" ); + + if ( m_bDisposingSubStorages ) + return; + + auto find = std::find_if(m_aExposedStorages.begin(), m_aExposedStorages.end(), + [&Source](const NamedStorages::value_type& rEntry) { return rEntry.second == Source.Source; }); + if (find != m_aExposedStorages.end()) + m_aExposedStorages.erase( find ); +} + +// ODatabaseModelImpl + +ODatabaseModelImpl::ODatabaseModelImpl( const Reference< XComponentContext >& _rxContext, ODatabaseContext& _rDBContext ) + :m_aContainer(4) + ,m_aMacroMode( *this ) + ,m_nImposedMacroExecMode( MacroExecMode::NEVER_EXECUTE ) + ,m_rDBContext( _rDBContext ) + ,m_refCount(0) + ,m_bModificationLock( false ) + ,m_bDocumentInitialized( false ) + ,m_nScriptingSignatureState(SignatureState::UNKNOWN) + ,m_aContext( _rxContext ) + ,m_nLoginTimeout(0) + ,m_bReadOnly(false) + ,m_bPasswordRequired(false) + ,m_bSuppressVersionColumns(true) + ,m_bModified(false) + ,m_bDocumentReadOnly(false) + ,m_bMacroCallsSeenWhileLoading(false) + ,m_pSharedConnectionManager(nullptr) + ,m_nControllerLockCount(0) +{ + // some kind of default + m_sConnectURL = "jdbc:"; + m_aTableFilter = { "%" }; + impl_construct_nothrow(); +} + +ODatabaseModelImpl::ODatabaseModelImpl( + const OUString& _rRegistrationName, + const Reference< XComponentContext >& _rxContext, + ODatabaseContext& _rDBContext + ) + :m_aContainer(4) + ,m_aMacroMode( *this ) + ,m_nImposedMacroExecMode( MacroExecMode::NEVER_EXECUTE ) + ,m_rDBContext( _rDBContext ) + ,m_refCount(0) + ,m_bModificationLock( false ) + ,m_bDocumentInitialized( false ) + ,m_nScriptingSignatureState(SignatureState::UNKNOWN) + ,m_aContext( _rxContext ) + ,m_sName(_rRegistrationName) + ,m_nLoginTimeout(0) + ,m_bReadOnly(false) + ,m_bPasswordRequired(false) + ,m_bSuppressVersionColumns(true) + ,m_bModified(false) + ,m_bDocumentReadOnly(false) + ,m_bMacroCallsSeenWhileLoading(false) + ,m_pSharedConnectionManager(nullptr) + ,m_nControllerLockCount(0) +{ + impl_construct_nothrow(); +} + +ODatabaseModelImpl::~ODatabaseModelImpl() +{ +} + +void ODatabaseModelImpl::impl_construct_nothrow() +{ + // create the property bag to hold the settings (also known as "Info" property) + try + { + // the set of property value types in the bag is limited: + Sequence< Type > aAllowedTypes({ + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType>::get(), + }); + + m_xSettings = PropertyBag::createWithTypes( m_aContext, aAllowedTypes, false/*AllowEmptyPropertyName*/, true/*AutomaticAddition*/ ); + + // insert the default settings + Reference< XPropertyContainer > xContainer( m_xSettings, UNO_QUERY_THROW ); + Reference< XSet > xSettingsSet( m_xSettings, UNO_QUERY_THROW ); + const AsciiPropertyValue* pSettings = getDefaultDataSourceSettings(); + for ( ; pSettings->AsciiName; ++pSettings ) + { + if ( !pSettings->DefaultValue.hasValue() ) + { + Property aProperty( + OUString::createFromAscii( pSettings->AsciiName ), + -1, + pSettings->ValueType, + PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT | PropertyAttribute::MAYBEVOID + ); + xSettingsSet->insert( Any( aProperty ) ); + } + else + { + xContainer->addProperty( + OUString::createFromAscii( pSettings->AsciiName ), + PropertyAttribute::BOUND | PropertyAttribute::MAYBEDEFAULT, + pSettings->DefaultValue + ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_rDBContext.appendAtTerminateListener(*this); +} + +namespace +{ + OUString lcl_getContainerStorageName_throw( ODatabaseModelImpl::ObjectType _eType ) + { + const char* pAsciiName( nullptr ); + switch ( _eType ) + { + case ODatabaseModelImpl::E_FORM: pAsciiName = "forms"; break; + case ODatabaseModelImpl::E_REPORT: pAsciiName = "reports"; break; + case ODatabaseModelImpl::E_QUERY: pAsciiName = "queries"; break; + case ODatabaseModelImpl::E_TABLE: pAsciiName = "tables"; break; + default: + throw RuntimeException(); + } + return OUString::createFromAscii( pAsciiName ); + } + + bool lcl_hasObjectWithMacros_throw( const ODefinitionContainer_Impl& _rObjectDefinitions, const Reference< XStorage >& _rxContainerStorage ) + { + bool bSomeDocHasMacros = false; + + for (auto const& objectDefinition : _rObjectDefinitions) + { + const TContentPtr& rDefinition( objectDefinition.second ); + const OUString& rPersistentName( rDefinition->m_aProps.sPersistentName ); + + if ( rPersistentName.isEmpty() ) + { // it's a logical sub folder used to organize the real objects + const ODefinitionContainer_Impl& rSubFoldersObjectDefinitions( dynamic_cast< const ODefinitionContainer_Impl& >( *rDefinition ) ); + bSomeDocHasMacros = lcl_hasObjectWithMacros_throw( rSubFoldersObjectDefinitions, _rxContainerStorage ); + if (bSomeDocHasMacros) + break; + continue; + } + + bSomeDocHasMacros = ODatabaseModelImpl::objectHasMacros( _rxContainerStorage, rPersistentName ); + if (bSomeDocHasMacros) + break; + } + return bSomeDocHasMacros; + } + + bool lcl_hasObjectsWithMacros_nothrow( ODatabaseModelImpl& _rModel, const ODatabaseModelImpl::ObjectType _eType ) + { + bool bSomeDocHasMacros = false; + + const OContentHelper_Impl& rContainerData( *_rModel.getObjectContainer( _eType ) ); + const ODefinitionContainer_Impl& rObjectDefinitions = dynamic_cast< const ODefinitionContainer_Impl& >( rContainerData ); + + try + { + Reference< XStorage > xContainerStorage( _rModel.getStorage( _eType ) ); + // note the READWRITE here: If the storage already existed before, then the OpenMode will + // be ignored, anyway. + // If the storage did not yet exist, then it will be created. If the database document + // is read-only, the OpenMode will be automatically downgraded to READ. Otherwise, + // the storage will in fact be created as READWRITE. While this is not strictly necessary + // for this particular use case here, it is required since the storage is *cached*, and + // later use cases will need the READWRITE mode. + + if ( xContainerStorage.is() ) + bSomeDocHasMacros = lcl_hasObjectWithMacros_throw( rObjectDefinitions, xContainerStorage ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + // be on the safe side: If we can't reliably determine whether there are macros, + // assume there actually are. Better this way, than the other way round. + bSomeDocHasMacros = true; + } + + return bSomeDocHasMacros; + } +} + +bool ODatabaseModelImpl::objectHasMacros( const Reference< XStorage >& _rxContainerStorage, const OUString& _rPersistentName ) +{ + OSL_PRECOND( _rxContainerStorage.is(), "ODatabaseModelImpl::objectHasMacros: this will crash!" ); + + bool bHasMacros = true; + try + { + if ( !_rxContainerStorage->hasByName( _rPersistentName ) ) + return false; + + Reference< XStorage > xObjectStor( _rxContainerStorage->openStorageElement( + _rPersistentName, ElementModes::READ ) ); + + bHasMacros = ::sfx2::DocumentMacroMode::storageHasMacros( xObjectStor ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bHasMacros; +} + +void ODatabaseModelImpl::reset() +{ + m_bReadOnly = false; + std::vector(4).swap(m_aContainer); + + if ( m_pStorageAccess.is() ) + { + m_pStorageAccess->dispose(); + m_pStorageAccess.clear(); + } +} + +void ODatabaseModelImpl::disposing( const css::lang::EventObject& Source ) +{ + Reference xCon(Source.Source,UNO_QUERY); + if ( xCon.is() ) + { + bool bStore = false; + for (OWeakConnectionArray::iterator i = m_aConnections.begin(); i != m_aConnections.end(); ) + { + css::uno::Reference< css::sdbc::XConnection > xIterConn ( *i ); + if ( !xIterConn.is()) + { + i = m_aConnections.erase(i); + } + else if ( xCon == xIterConn ) + { + *i = css::uno::WeakReference< css::sdbc::XConnection >(); + bStore = true; + break; + } else + ++i; + } + + if ( bStore ) + commitRootStorage(); + } + else + { + OSL_FAIL( "ODatabaseModelImpl::disposing: where does this come from?" ); + } +} + +void ODatabaseModelImpl::clearConnections() +{ + OWeakConnectionArray aConnections; + aConnections.swap( m_aConnections ); + + Reference< XConnection > xConn; + for (auto const& connection : aConnections) + { + xConn = connection; + if ( xConn.is() ) + { + try + { + xConn->close(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + m_pSharedConnectionManager = nullptr; + m_xSharedConnectionManager = nullptr; +} + +void ODatabaseModelImpl::dispose() +{ + // dispose the data source and the model + try + { + Reference< XDataSource > xDS( m_xDataSource ); + ::comphelper::disposeComponent( xDS ); + + Reference< XModel > xModel( m_xModel ); + ::comphelper::disposeComponent( xModel ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_xDataSource = WeakReference(); + m_xModel = WeakReference< XModel >(); + + for (auto const& elem : m_aContainer) + { + if ( elem ) + elem->m_pDataSource = nullptr; + } + m_aContainer.clear(); + + clearConnections(); + + m_xNumberFormatsSupplier = nullptr; + + try + { + bool bCouldStore = commitEmbeddedStorage( true ); + // "true" means that committing the embedded storage should not trigger committing the root + // storage. This is because we are going to commit the root storage ourself, anyway + disposeStorages(); + if ( bCouldStore ) + commitRootStorage(); + + impl_switchToStorage_throw( nullptr ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( m_pStorageAccess.is() ) + { + m_pStorageAccess->dispose(); + m_pStorageAccess.clear(); + } +} + +const Reference< XNumberFormatsSupplier > & ODatabaseModelImpl::getNumberFormatsSupplier() +{ + if (!m_xNumberFormatsSupplier.is()) + { + // the arguments : the work locale of the current user + Locale aLocale( LanguageTag::convertToLocale( utl::ConfigManager::getWorkLocale(), false)); + + m_xNumberFormatsSupplier.set( NumberFormatsSupplier::createWithLocale( m_aContext, aLocale ) ); + } + return m_xNumberFormatsSupplier; +} + +void ODatabaseModelImpl::setDocFileLocation( const OUString& i_rLoadedFrom ) +{ + ENSURE_OR_THROW( !i_rLoadedFrom.isEmpty(), "invalid URL" ); + m_sDocFileLocation = i_rLoadedFrom; +} + +void ODatabaseModelImpl::setResource( const OUString& i_rDocumentURL, const Sequence< PropertyValue >& _rArgs ) +{ + ENSURE_OR_THROW( !i_rDocumentURL.isEmpty(), "invalid URL" ); + + ::comphelper::NamedValueCollection aMediaDescriptor( _rArgs ); +#if OSL_DEBUG_LEVEL > 0 + if ( aMediaDescriptor.has( "SalvagedFile" ) ) + { + OUString sSalvagedFile( aMediaDescriptor.getOrDefault( "SalvagedFile", OUString() ) ); + // If SalvagedFile is an empty string, this indicates "the document is being recovered, but i_rDocumentURL already + // is the real document URL, not the temporary document location" + if ( sSalvagedFile.isEmpty() ) + sSalvagedFile = i_rDocumentURL; + + OSL_ENSURE( sSalvagedFile == i_rDocumentURL, "ODatabaseModelImpl::setResource: inconsistency!" ); + // nowadays, setResource should only be called with the logical URL of the document + } +#endif + + m_aMediaDescriptor = stripLoadArguments( aMediaDescriptor ); + + impl_switchToLogicalURL( i_rDocumentURL ); +} + +::comphelper::NamedValueCollection ODatabaseModelImpl::stripLoadArguments( const ::comphelper::NamedValueCollection& _rArguments ) +{ + OSL_ENSURE( !_rArguments.has( "Model" ), "ODatabaseModelImpl::stripLoadArguments: this is suspicious (1)!" ); + OSL_ENSURE( !_rArguments.has( "ViewName" ), "ODatabaseModelImpl::stripLoadArguments: this is suspicious (2)!" ); + + ::comphelper::NamedValueCollection aMutableArgs( _rArguments ); + aMutableArgs.remove( "Model" ); + aMutableArgs.remove( "ViewName" ); + return aMutableArgs; +} + +void ODatabaseModelImpl::disposeStorages() +{ + getDocumentStorageAccess()->disposeStorages(); +} + +Reference< XSingleServiceFactory > ODatabaseModelImpl::createStorageFactory() const +{ + return StorageFactory::create( m_aContext ); +} + +void ODatabaseModelImpl::commitRootStorage() +{ + Reference< XStorage > xStorage( getOrCreateRootStorage() ); + bool bSuccess = commitStorageIfWriteable_ignoreErrors( xStorage ); + SAL_WARN_IF(!bSuccess && xStorage.is(), "dbaccess", + "ODatabaseModelImpl::commitRootStorage: could not commit the storage!"); +} + +Reference< XStorage > const & ODatabaseModelImpl::getOrCreateRootStorage() +{ + if ( !m_xDocumentStorage.is() ) + { + Reference< XSingleServiceFactory> xStorageFactory = StorageFactory::create( m_aContext ); + Any aSource = m_aMediaDescriptor.get( "Stream" ); + if ( !aSource.hasValue() ) + aSource = m_aMediaDescriptor.get( "InputStream" ); + if ( !aSource.hasValue() && !m_sDocFileLocation.isEmpty() ) + aSource <<= m_sDocFileLocation; + // TODO: shouldn't we also check URL? + + OSL_ENSURE( aSource.hasValue(), "ODatabaseModelImpl::getOrCreateRootStorage: no source to create the storage from!" ); + + if ( aSource.hasValue() ) + { + Sequence< Any > aStorageCreationArgs{ aSource, Any(ElementModes::READWRITE) }; + + Reference< XStorage > xDocumentStorage; + OUString sURL; + aSource >>= sURL; + // Don't try to load a meta-URL as-is. + if (!sURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:")) + { + try + { + xDocumentStorage.set( xStorageFactory->createInstanceWithArguments( aStorageCreationArgs ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + m_bDocumentReadOnly = true; + aStorageCreationArgs.getArray()[1] <<= ElementModes::READ; + try + { + xDocumentStorage.set( xStorageFactory->createInstanceWithArguments( aStorageCreationArgs ), UNO_QUERY_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + impl_switchToStorage_throw( xDocumentStorage ); + } + } + return m_xDocumentStorage.getTyped(); +} + +DocumentStorageAccess* ODatabaseModelImpl::getDocumentStorageAccess() +{ + if ( !m_pStorageAccess.is() ) + { + m_pStorageAccess = new DocumentStorageAccess( *this ); + } + return m_pStorageAccess.get(); +} + +void ODatabaseModelImpl::modelIsDisposing( const bool _wasInitialized, ResetModelAccess ) +{ + m_xModel.clear(); + + // Basic libraries and Dialog libraries are a model facet, though held at this impl class. + // They automatically dispose themself when the model they belong to is being disposed. + // So, to not be tempted to do anything with them, again, we reset them. + m_xBasicLibraries.clear(); + m_xDialogLibraries.clear(); + + m_bDocumentInitialized = _wasInitialized; +} + +Reference< XDocumentSubStorageSupplier > ODatabaseModelImpl::getDocumentSubStorageSupplier() +{ + return getDocumentStorageAccess(); +} + +bool ODatabaseModelImpl::commitEmbeddedStorage( bool _bPreventRootCommits ) +{ + return getDocumentStorageAccess()->commitEmbeddedStorage( _bPreventRootCommits ); +} + +bool ODatabaseModelImpl::commitStorageIfWriteable_ignoreErrors( const Reference< XStorage >& _rxStorage ) +{ + bool bTryToPreserveScriptSignature = false; + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + OUString sTmpFileUrl = aTempFile.GetURL(); + SignatureState aSignatureState = getScriptingSignatureState(); + OUString sLocation = getDocFileLocation(); + bool bIsEmbedded = sLocation.startsWith("vnd.sun.star.pkg:") && sLocation.endsWith("/EmbeddedDatabase"); + if (!bIsEmbedded && !sLocation.isEmpty() + && (aSignatureState == SignatureState::OK || aSignatureState == SignatureState::NOTVALIDATED + || aSignatureState == SignatureState::INVALID + || aSignatureState == SignatureState::UNKNOWN)) + { + bTryToPreserveScriptSignature = true; + // We need to first save the file (which removes the macro signature), then add the macro signature again. + // For that, we need a temporary copy of the original file. + osl::File::RC rc = osl::File::copy(sLocation, sTmpFileUrl); + if (rc != osl::FileBase::E_None) + throw uno::RuntimeException("Could not create temp file"); + } + + bool bSuccess = false; + try + { + bSuccess = tools::stor::commitStorageIfWriteable( _rxStorage ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // Preserve script signature if the script has not changed + if (bTryToPreserveScriptSignature) + { + OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(_rxStorage)); + uno::Reference xDDSigns; + try + { + xDDSigns = security::DocumentDigitalSignatures::createWithVersion( + comphelper::getProcessComponentContext(), aODFVersion); + + const OUString aScriptSignName + = xDDSigns->getScriptingContentSignatureDefaultStreamName(); + + if (!aScriptSignName.isEmpty()) + { + Reference xReadOrig + = comphelper::OStorageHelper::GetStorageOfFormatFromURL( + ZIP_STORAGE_FORMAT_STRING, sTmpFileUrl, ElementModes::READ); + if (!xReadOrig.is()) + throw uno::RuntimeException("Could not read " + sTmpFileUrl); + uno::Reference xMetaInf + = xReadOrig->openStorageElement("META-INF", embed::ElementModes::READ); + + uno::Reference xTargetMetaInf + = _rxStorage->openStorageElement("META-INF", embed::ElementModes::READWRITE); + if (xMetaInf.is() && xTargetMetaInf.is() && xMetaInf->hasByName(aScriptSignName)) + { + xMetaInf->copyElementTo(aScriptSignName, xTargetMetaInf, aScriptSignName); + + uno::Reference xTransact(xTargetMetaInf, + uno::UNO_QUERY); + if (xTransact.is()) + xTransact->commit(); + + xTargetMetaInf->dispose(); + + // now check the copied signature + uno::Sequence aInfos + = xDDSigns->verifyScriptingContentSignatures( + _rxStorage, uno::Reference()); + SignatureState nState = DocumentSignatures::getSignatureState(aInfos); + if (nState == SignatureState::OK || nState == SignatureState::NOTVALIDATED + || nState == SignatureState::PARTIAL_OK) + { + // commit the ZipStorage from target medium + xTransact.set(_rxStorage, uno::UNO_QUERY); + if (xTransact.is()) + xTransact->commit(); + } + else + { + SAL_WARN("dbaccess", "An invalid signature was copied!"); + } + } + } + } + catch (uno::Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", ""); + } + } + + return bSuccess; +} + +void ODatabaseModelImpl::setModified( bool _bModified ) +{ + if ( isModifyLocked() ) + return; + + try + { + Reference< XModifiable > xModi( m_xModel.get(), UNO_QUERY ); + if ( xModi.is() ) + xModi->setModified( _bModified ); + else + m_bModified = _bModified; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference ODatabaseModelImpl::getOrCreateDataSource() +{ + Reference xDs = m_xDataSource; + if ( !xDs.is() ) + { + xDs = new ODatabaseSource(this); + m_xDataSource = xDs; + } + return xDs; +} + +Reference< XModel> ODatabaseModelImpl::getModel_noCreate() const +{ + return m_xModel; +} + +Reference< XModel > ODatabaseModelImpl::createNewModel_deliverOwnership() +{ + Reference< XModel > xModel( m_xModel ); + OSL_PRECOND( !xModel.is(), "ODatabaseModelImpl::createNewModel_deliverOwnership: not to be called if there already is a model!" ); + if ( !xModel.is() ) + { + bool bHadModelBefore = m_bDocumentInitialized; + + xModel = ODatabaseDocument::createDatabaseDocument( this, ODatabaseDocument::FactoryAccess() ); + m_xModel = xModel; + + try + { + Reference< XGlobalEventBroadcaster > xModelCollection = theGlobalEventBroadcaster::get( m_aContext ); + xModelCollection->insert( Any( xModel ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( bHadModelBefore ) + { + // do an attachResources + // In case the document is loaded regularly, this is not necessary, as our loader will do it. + // However, in case that the document is implicitly created by asking the data source for the document, + // then nobody would call the doc's attachResource. So, we do it here, to ensure it's in a proper + // state, fires all events, and so on. + // #i105505# + xModel->attachResource( xModel->getURL(), m_aMediaDescriptor.getPropertyValues() ); + } + } + return xModel; +} + +void ODatabaseModelImpl::acquire() +{ + osl_atomic_increment(&m_refCount); +} + +void ODatabaseModelImpl::release() +{ + if ( osl_atomic_decrement(&m_refCount) == 0 ) + { + acquire(); // prevent multiple releases + m_rDBContext.removeFromTerminateListener(*this); + dispose(); + m_rDBContext.storeTransientProperties(*this); + if (!m_sDocumentURL.isEmpty()) + m_rDBContext.revokeDatabaseDocument(*this); + delete this; + } +} + +void ODatabaseModelImpl::commitStorages() +{ + getDocumentStorageAccess()->commitStorages(); +} + +Reference< XStorage > ODatabaseModelImpl::getStorage( const ObjectType _eType ) +{ + return getDocumentStorageAccess()->getDocumentSubStorage( getObjectContainerStorageName( _eType ), + css::embed::ElementModes::READWRITE ); +} + +const AsciiPropertyValue* ODatabaseModelImpl::getDefaultDataSourceSettings() +{ + static const AsciiPropertyValue aKnownSettings[] = + { + // known JDBC settings + AsciiPropertyValue( "JavaDriverClass", Any( OUString() ) ), + AsciiPropertyValue( "JavaDriverClassPath", Any( OUString() ) ), + AsciiPropertyValue( "IgnoreCurrency", Any( false ) ), + // known settings for file-based drivers + AsciiPropertyValue( "Extension", Any( OUString() ) ), + AsciiPropertyValue( "CharSet", Any( OUString() ) ), + AsciiPropertyValue( "HeaderLine", Any( true ) ), + AsciiPropertyValue( "FieldDelimiter", Any( OUString( "," ) ) ), + AsciiPropertyValue( "StringDelimiter", Any( OUString( "\"" ) ) ), + AsciiPropertyValue( "DecimalDelimiter", Any( OUString( "." ) ) ), + AsciiPropertyValue( "ThousandDelimiter", Any( OUString() ) ), + AsciiPropertyValue( "ShowDeleted", Any( false ) ), + // known ODBC settings + AsciiPropertyValue( "SystemDriverSettings", Any( OUString() ) ), + AsciiPropertyValue( "UseCatalog", Any( false ) ), + AsciiPropertyValue( "TypeInfoSettings", Any( Sequence< Any >()) ), + // settings related to auto increment handling + AsciiPropertyValue( "AutoIncrementCreation", Any( OUString() ) ), + AsciiPropertyValue( "AutoRetrievingStatement", Any( OUString() ) ), + AsciiPropertyValue( "IsAutoRetrievingEnabled", Any( false ) ), + // known LDAP driver settings + AsciiPropertyValue( "HostName", Any( OUString() ) ), + AsciiPropertyValue( "PortNumber", Any( sal_Int32(389) ) ), + AsciiPropertyValue( "BaseDN", Any( OUString() ) ), + AsciiPropertyValue( "MaxRowCount", Any( sal_Int32(100) ) ), + // known MySQLNative driver settings + AsciiPropertyValue( "LocalSocket", Any( OUString() ) ), + AsciiPropertyValue( "NamedPipe", Any( OUString() ) ), + // misc known driver settings + AsciiPropertyValue( "ParameterNameSubstitution", Any( false ) ), + AsciiPropertyValue( "AddIndexAppendix", Any( true ) ), + AsciiPropertyValue( "IgnoreDriverPrivileges", Any( true ) ), + AsciiPropertyValue( "ImplicitCatalogRestriction", ::cppu::UnoType< OUString >::get() ), + AsciiPropertyValue( "ImplicitSchemaRestriction", ::cppu::UnoType< OUString >::get() ), + AsciiPropertyValue( "PrimaryKeySupport", ::cppu::UnoType< sal_Bool >::get() ), + AsciiPropertyValue( "ShowColumnDescription", Any( false ) ), + // known SDB level settings + AsciiPropertyValue( "NoNameLengthLimit", Any( false ) ), + AsciiPropertyValue( "AppendTableAliasName", Any( false ) ), + AsciiPropertyValue( "GenerateASBeforeCorrelationName", Any( false ) ), + AsciiPropertyValue( "ColumnAliasInOrderBy", Any( true ) ), + AsciiPropertyValue( "EnableSQL92Check", Any( false ) ), + AsciiPropertyValue( "BooleanComparisonMode", Any( BooleanComparisonMode::EQUAL_INTEGER ) ), + AsciiPropertyValue( "TableTypeFilterMode", Any( sal_Int32(3) ) ), + AsciiPropertyValue( "RespectDriverResultSetType", Any( false ) ), + AsciiPropertyValue( "UseSchemaInSelect", Any( true ) ), + AsciiPropertyValue( "UseCatalogInSelect", Any( true ) ), + AsciiPropertyValue( "EnableOuterJoinEscape", Any( true ) ), + AsciiPropertyValue( "PreferDosLikeLineEnds", Any( false ) ), + AsciiPropertyValue( "FormsCheckRequiredFields", Any( true ) ), + AsciiPropertyValue( "EscapeDateTime", Any( true ) ), + + // known services to handle database tasks + AsciiPropertyValue( "TableAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "TableRenameServiceName", Any( OUString() ) ), + AsciiPropertyValue( "ViewAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "ViewAccessServiceName", Any( OUString() ) ), + AsciiPropertyValue( "CommandDefinitions", Any( OUString() ) ), + AsciiPropertyValue( "Forms", Any( OUString() ) ), + AsciiPropertyValue( "Reports", Any( OUString() ) ), + AsciiPropertyValue( "KeyAlterationServiceName", Any( OUString() ) ), + AsciiPropertyValue( "IndexAlterationServiceName", Any( OUString() ) ), + + AsciiPropertyValue() + }; + return aKnownSettings; +} + +TContentPtr& ODatabaseModelImpl::getObjectContainer( ObjectType _eType ) +{ + OSL_PRECOND( _eType >= E_FORM && _eType <= E_TABLE, "ODatabaseModelImpl::getObjectContainer: illegal index!" ); + TContentPtr& rContentPtr = m_aContainer[ _eType ]; + + if ( !rContentPtr ) + { + rContentPtr = std::make_shared(); + rContentPtr->m_pDataSource = this; + rContentPtr->m_aProps.aTitle = lcl_getContainerStorageName_throw( _eType ); + } + return rContentPtr; +} + +bool ODatabaseModelImpl::adjustMacroMode_AutoReject() +{ + return m_aMacroMode.adjustMacroMode( nullptr ); +} + +bool ODatabaseModelImpl::checkMacrosOnLoading() +{ + Reference< XInteractionHandler > xInteraction; + xInteraction = m_aMediaDescriptor.getOrDefault( "InteractionHandler", xInteraction ); + return m_aMacroMode.checkMacrosOnLoading( xInteraction ); +} + +void ODatabaseModelImpl::resetMacroExecutionMode() +{ + m_aMacroMode = ::sfx2::DocumentMacroMode( *this ); +} + +Reference< XStorageBasedLibraryContainer > ODatabaseModelImpl::getLibraryContainer( bool _bScript ) +{ + Reference< XStorageBasedLibraryContainer >& rxContainer( _bScript ? m_xBasicLibraries : m_xDialogLibraries ); + if ( rxContainer.is() ) + return rxContainer; + + Reference< XStorageBasedDocument > xDocument( getModel_noCreate(), UNO_QUERY_THROW ); + // this is only to be called if there already exists a document model - in fact, it is + // to be called by the document model only + + try + { + Reference< XStorageBasedLibraryContainer > (*Factory)( const Reference< XComponentContext >&, const Reference< XStorageBasedDocument >&) + = _bScript ? &DocumentScriptLibraryContainer::create : &DocumentDialogLibraryContainer::create; + + rxContainer.set( + (*Factory)( m_aContext, xDocument ), + UNO_SET_THROW + ); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + throw WrappedTargetRuntimeException( + OUString(), + xDocument, + ::cppu::getCaughtException() + ); + } + return rxContainer; +} + +void ODatabaseModelImpl::storeLibraryContainersTo( const Reference< XStorage >& _rxToRootStorage ) +{ + if ( m_xBasicLibraries.is() ) + m_xBasicLibraries->storeLibrariesToStorage( _rxToRootStorage ); + + if ( m_xDialogLibraries.is() ) + m_xDialogLibraries->storeLibrariesToStorage( _rxToRootStorage ); +} + +Reference< XStorage > ODatabaseModelImpl::switchToStorage( const Reference< XStorage >& _rxNewRootStorage ) +{ + if ( !_rxNewRootStorage.is() ) + throw IllegalArgumentException(); + + return impl_switchToStorage_throw( _rxNewRootStorage ); +} + +namespace +{ + void lcl_modifyListening( ::sfx2::IModifiableDocument& _rDocument, + const Reference< XStorage >& _rxStorage, ::rtl::Reference< ::sfx2::DocumentStorageModifyListener >& _inout_rListener, + comphelper::SolarMutex& _rMutex, bool _bListen ) + { + Reference< XModifiable > xModify( _rxStorage, UNO_QUERY ); + OSL_ENSURE( xModify.is() || !_rxStorage.is(), "lcl_modifyListening: storage can't notify us!" ); + + if ( xModify.is() && !_bListen && _inout_rListener.is() ) + { + xModify->removeModifyListener( _inout_rListener ); + } + + if ( _inout_rListener.is() ) + { + _inout_rListener->dispose(); + _inout_rListener = nullptr; + } + + if ( xModify.is() && _bListen ) + { + _inout_rListener = new ::sfx2::DocumentStorageModifyListener( _rDocument, _rMutex ); + xModify->addModifyListener( _inout_rListener ); + } + } +} + +namespace +{ + void lcl_rebaseScriptStorage_throw( const Reference< XStorageBasedLibraryContainer >& _rxContainer, + const Reference< XStorage >& _rxNewRootStorage ) + { + if ( _rxContainer.is() ) + { + if ( _rxNewRootStorage.is() ) + _rxContainer->setRootStorage( _rxNewRootStorage ); +// else + // TODO: what to do here? dispose the container? + } + } +} + +Reference< XStorage > const & ODatabaseModelImpl::impl_switchToStorage_throw( const Reference< XStorage >& _rxNewRootStorage ) +{ + // stop listening for modifications at the old storage + lcl_modifyListening( *this, m_xDocumentStorage.getTyped(), m_pStorageModifyListener, Application::GetSolarMutex(), false ); + + // set new storage + m_xDocumentStorage.reset( _rxNewRootStorage, SharedStorage::TakeOwnership ); + + // start listening for modifications + lcl_modifyListening( *this, m_xDocumentStorage.getTyped(), m_pStorageModifyListener, Application::GetSolarMutex(), true ); + + // forward new storage to Basic and Dialog library containers + lcl_rebaseScriptStorage_throw( m_xBasicLibraries, m_xDocumentStorage.getTyped() ); + lcl_rebaseScriptStorage_throw( m_xDialogLibraries, m_xDocumentStorage.getTyped() ); + + m_bReadOnly = !tools::stor::storageIsWritable_nothrow( m_xDocumentStorage.getTyped() ); + // TODO: our data source, if it exists, must broadcast the change of its ReadOnly property + + return m_xDocumentStorage.getTyped(); +} + +void ODatabaseModelImpl::impl_switchToLogicalURL( const OUString& i_rDocumentURL ) +{ + if ( i_rDocumentURL == m_sDocumentURL ) + return; + + const OUString sOldURL( m_sDocumentURL ); + // update our name, if necessary + if ( ( m_sName == m_sDocumentURL ) // our name is our old URL + || ( m_sName.isEmpty() ) // we do not have a name, yet (i.e. are not registered at the database context) + ) + { + INetURLObject aURL( i_rDocumentURL ); + if ( aURL.GetProtocol() != INetProtocol::NotValid ) + { + m_sName = i_rDocumentURL; + // TODO: our data source must broadcast the change of the Name property + } + } + + // remember URL + m_sDocumentURL = i_rDocumentURL; + + // update our location, if necessary + if ( m_sDocFileLocation.isEmpty() ) + m_sDocFileLocation = m_sDocumentURL; + + // register at the database context, or change registration + if (!sOldURL.isEmpty()) + m_rDBContext.databaseDocumentURLChange( sOldURL, m_sDocumentURL ); + else + m_rDBContext.registerDatabaseDocument( *this ); +} + +OUString ODatabaseModelImpl::getObjectContainerStorageName( const ObjectType _eType ) +{ + return lcl_getContainerStorageName_throw( _eType ); +} + +sal_Int16 ODatabaseModelImpl::getCurrentMacroExecMode() const +{ + sal_Int16 nCurrentMode = MacroExecMode::NEVER_EXECUTE; + try + { + nCurrentMode = m_aMediaDescriptor.getOrDefault( "MacroExecutionMode", nCurrentMode ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return nCurrentMode; +} + +void ODatabaseModelImpl::setCurrentMacroExecMode( sal_uInt16 nMacroMode ) +{ + m_aMediaDescriptor.put( "MacroExecutionMode", nMacroMode ); +} + +OUString ODatabaseModelImpl::getDocumentLocation() const +{ + return getURL(); + // formerly, we returned getDocFileLocation here, which is the location of the file from which we + // recovered the "real" document. + // However, during CWS autorecovery evolving, we clarified (with MAV/MT) the role of XModel::getURL and + // XStorable::getLocation. In this course, we agreed that for a macro security check, the *document URL* + // (not the recovery file URL) is to be used: The recovery file lies in the backup folder, and by definition, + // this folder is considered to be secure. So, the document URL needs to be used to decide about the security. +} + +ODatabaseModelImpl::EmbeddedMacros ODatabaseModelImpl::determineEmbeddedMacros() +{ + if ( !m_aEmbeddedMacros ) + { + if ( ::sfx2::DocumentMacroMode::storageHasMacros( getOrCreateRootStorage() ) ) + { + m_aEmbeddedMacros = eDocumentWideMacros; + } + else if ( lcl_hasObjectsWithMacros_nothrow( *this, E_FORM ) + || lcl_hasObjectsWithMacros_nothrow( *this, E_REPORT ) + ) + { + m_aEmbeddedMacros = eSubDocumentMacros; + } + else + { + m_aEmbeddedMacros = eNoMacros; + } + } + return *m_aEmbeddedMacros; +} + +bool ODatabaseModelImpl::documentStorageHasMacros() const +{ + const_cast< ODatabaseModelImpl* >( this )->determineEmbeddedMacros(); + return ( *m_aEmbeddedMacros != eNoMacros ); +} + +bool ODatabaseModelImpl::macroCallsSeenWhileLoading() const +{ + return m_bMacroCallsSeenWhileLoading; +} + +Reference< XEmbeddedScripts > ODatabaseModelImpl::getEmbeddedDocumentScripts() const +{ + return Reference< XEmbeddedScripts >( getModel_noCreate(), UNO_QUERY ); +} + +SignatureState ODatabaseModelImpl::getScriptingSignatureState() +{ + return m_nScriptingSignatureState; +} + +bool ODatabaseModelImpl::hasTrustedScriptingSignature(bool bAllowUIToAddAuthor) +{ + bool bResult = false; + + try + { + // Don't use m_xDocumentStorage, that somehow has an incomplete storage representation + // which leads to signatures not being found + Reference xStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL( + ZIP_STORAGE_FORMAT_STRING, m_sDocFileLocation, ElementModes::READ); + + OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(getOrCreateRootStorage())); + uno::Reference xSigner( + security::DocumentDigitalSignatures::createWithVersion( + comphelper::getProcessComponentContext(), aODFVersion)); + const uno::Sequence aInfo + = xSigner->verifyScriptingContentSignatures(xStorage, + uno::Reference()); + + if (!aInfo.hasElements()) + return false; + + m_nScriptingSignatureState = DocumentSignatures::getSignatureState(aInfo); + if (m_nScriptingSignatureState == SignatureState::OK + || m_nScriptingSignatureState == SignatureState::NOTVALIDATED) + { + bResult = std::any_of(aInfo.begin(), aInfo.end(), + [&xSigner](const security::DocumentSignatureInformation& rInfo) { + return xSigner->isAuthorTrusted(rInfo.Signer); + }); + } + + if (!bResult && bAllowUIToAddAuthor) + { + Reference xInteraction; + xInteraction = m_aMediaDescriptor.getOrDefault("InteractionHandler", xInteraction); + if (xInteraction.is()) + { + task::DocumentMacroConfirmationRequest aRequest; + aRequest.DocumentURL = m_sDocFileLocation; + aRequest.DocumentStorage = xStorage; + aRequest.DocumentSignatureInformation = aInfo; + aRequest.DocumentVersion = aODFVersion; + aRequest.Classification = task::InteractionClassification_QUERY; + bResult = SfxMedium::CallApproveHandler(xInteraction, uno::Any(aRequest), true); + } + } + } + catch (uno::Exception&) + { + } + + return bResult; +} + +void ODatabaseModelImpl::storageIsModified() +{ + setModified( true ); +} + +ModelDependentComponent::ModelDependentComponent( const ::rtl::Reference< ODatabaseModelImpl >& _model ) + :m_pImpl( _model ) +{ +} + +ModelDependentComponent::~ModelDependentComponent() +{ +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/SharedConnection.cxx b/dbaccess/source/core/dataaccess/SharedConnection.cxx new file mode 100644 index 000000000..31a8e68ba --- /dev/null +++ b/dbaccess/source/core/dataaccess/SharedConnection.cxx @@ -0,0 +1,154 @@ +/* -*- 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 "SharedConnection.hxx" +#include + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::container; +using namespace connectivity; + +OSharedConnection::OSharedConnection(Reference& _rxProxyConnection) + : OSharedConnection_BASE(m_aMutex) +{ + setDelegation(_rxProxyConnection, m_refCount); +} + +OSharedConnection::~OSharedConnection() {} + +void SAL_CALL OSharedConnection::disposing() +{ + OSharedConnection_BASE::disposing(); + OConnectionWrapper::disposing(); +} + +Reference SAL_CALL OSharedConnection::createStatement() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->createStatement(); +} + +Reference SAL_CALL OSharedConnection::prepareStatement(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->prepareStatement(sql); +} + +Reference SAL_CALL OSharedConnection::prepareCall(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->prepareCall(sql); +} + +OUString SAL_CALL OSharedConnection::nativeSQL(const OUString& sql) +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->nativeSQL(sql); +} + +sal_Bool SAL_CALL OSharedConnection::getAutoCommit() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getAutoCommit(); +} + +void SAL_CALL OSharedConnection::commit() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + m_xConnection->commit(); +} + +void SAL_CALL OSharedConnection::rollback() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + m_xConnection->rollback(); +} + +sal_Bool SAL_CALL OSharedConnection::isClosed() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (!m_xConnection.is()) + return true; + + return m_xConnection->isClosed(); +} + +Reference SAL_CALL OSharedConnection::getMetaData() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getMetaData(); +} + +sal_Bool SAL_CALL OSharedConnection::isReadOnly() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->isReadOnly(); +} + +OUString SAL_CALL OSharedConnection::getCatalog() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getCatalog(); +} + +sal_Int32 SAL_CALL OSharedConnection::getTransactionIsolation() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getTransactionIsolation(); +} + +Reference SAL_CALL OSharedConnection::getTypeMap() +{ + ::osl::MutexGuard aGuard(m_aMutex); + checkDisposed(rBHelper.bDisposed); + + return m_xConnection->getTypeMap(); +} + +IMPLEMENT_GET_IMPLEMENTATION_ID(OSharedConnection) + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/SharedConnection.hxx b/dbaccess/source/core/dataaccess/SharedConnection.hxx new file mode 100644 index 000000000..ea6e961cf --- /dev/null +++ b/dbaccess/source/core/dataaccess/SharedConnection.hxx @@ -0,0 +1,118 @@ +/* -*- 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 . + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace dbaccess +{ + // OSharedConnection: This class implements a simple forwarding of connection calls. + // All methods will be forwarded with exception of the set methods, which are not allowed + // to be called on shared connections. Instances of this class will be created when the + // datasource is asked for not isolated connection. + typedef ::cppu::WeakComponentImplHelper< css::sdbc::XConnection + > OSharedConnection_BASE; + typedef ::connectivity::OConnectionWrapper OSharedConnection_BASE2; + + class OSharedConnection : public ::cppu::BaseMutex + , public OSharedConnection_BASE + , public OSharedConnection_BASE2 + { + protected: + virtual void SAL_CALL disposing() override; + virtual ~OSharedConnection() override; + public: + explicit OSharedConnection(css::uno::Reference< css::uno::XAggregation >& _rxProxyConnection); + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + virtual void SAL_CALL acquire() noexcept override { OSharedConnection_BASE::acquire(); } + virtual void SAL_CALL release() noexcept override { OSharedConnection_BASE::release(); } + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override + { + return ::comphelper::concatSequences( + OSharedConnection_BASE::getTypes(), + OSharedConnection_BASE2::getTypes() + ); + } + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& _rType ) override + { + css::uno::Any aReturn = OSharedConnection_BASE::queryInterface(_rType); + if ( !aReturn.hasValue() ) + aReturn = OSharedConnection_BASE2::queryInterface(_rType); + return aReturn; + } + + // XCloseable + virtual void SAL_CALL close( ) override + { + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::connectivity::checkDisposed(rBHelper.bDisposed); + } + dispose(); + } + + // XConnection + virtual void SAL_CALL setAutoCommit( sal_Bool /*autoCommit*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setReadOnly( sal_Bool /*readOnly*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setCatalog( const OUString& /*catalog*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setTransactionIsolation( sal_Int32 /*level*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& /*typeMap*/ ) override + { + throw css::sdbc::SQLException("This call is not allowed when sharing connections.",*this,"S10000",0,css::uno::Any()); + } + // XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx b/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx new file mode 100644 index 000000000..a03caea57 --- /dev/null +++ b/dbaccess/source/core/dataaccess/bookmarkcontainer.cxx @@ -0,0 +1,301 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +// OBookmarkContainer + +OBookmarkContainer::OBookmarkContainer(OWeakObject& _rParent, Mutex& _rMutex) + :m_rParent(_rParent) + ,m_aContainerListeners(_rMutex) + ,m_rMutex(_rMutex) +{ +} + + +void SAL_CALL OBookmarkContainer::acquire( ) noexcept +{ + m_rParent.acquire(); +} + +void SAL_CALL OBookmarkContainer::release( ) noexcept +{ + m_rParent.release(); +} + +OBookmarkContainer::~OBookmarkContainer() +{ +} + +// XServiceInfo +OUString SAL_CALL OBookmarkContainer::getImplementationName( ) +{ + return "com.sun.star.comp.dba.OBookmarkContainer"; +} + +sal_Bool SAL_CALL OBookmarkContainer::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > SAL_CALL OBookmarkContainer::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DefinitionContainer" }; +} + +// XNameContainer +void SAL_CALL OBookmarkContainer::insertByName( const OUString& _rName, const Any& aElement ) +{ + MutexGuard aGuard(m_rMutex); + + if (checkExistence(_rName)) + throw ElementExistException(); + + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + // approve the new object + OUString sNewLink; + if (!(aElement >>= sNewLink)) + throw IllegalArgumentException(); + + implAppend(_rName, sNewLink); + + // notify the listeners + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sNewLink), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); + } +} + +void SAL_CALL OBookmarkContainer::removeByName( const OUString& _rName ) +{ + OUString sOldBookmark; + { + MutexGuard aGuard(m_rMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + // the old element (for the notifications) + sOldBookmark = m_aBookmarks[_rName]; + + // do the removal + implRemove(_rName); + } + + // notify the listeners + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sOldBookmark), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvent ); + } +} + +// XNameReplace +void SAL_CALL OBookmarkContainer::replaceByName( const OUString& _rName, const Any& aElement ) +{ + ClearableMutexGuard aGuard(m_rMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + // do we have such an element? + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + // approve the new object + OUString sNewLink; + if (!(aElement >>= sNewLink)) + throw IllegalArgumentException(); + + // the old element (for the notifications) + OUString sOldLink = m_aBookmarks[_rName]; + + // do the replace + implReplace(_rName, sNewLink); + + // notify the listeners + aGuard.clear(); + if (m_aContainerListeners.getLength()) + { + ContainerEvent aEvent(*this, Any(_rName), Any(sNewLink), Any(sOldLink)); + m_aContainerListeners.notifyEach( &XContainerListener::elementReplaced, aEvent ); + } +} + +void SAL_CALL OBookmarkContainer::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + MutexGuard aGuard(m_rMutex); + if (_rxListener.is()) + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL OBookmarkContainer::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + MutexGuard aGuard(m_rMutex); + if (_rxListener.is()) + m_aContainerListeners.removeInterface(_rxListener); +} + +// XElementAccess +Type SAL_CALL OBookmarkContainer::getElementType( ) +{ + return ::cppu::UnoType::get(); +} + +sal_Bool SAL_CALL OBookmarkContainer::hasElements( ) +{ + MutexGuard aGuard(m_rMutex); + return !m_aBookmarks.empty(); +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL OBookmarkContainer::createEnumeration( ) +{ + MutexGuard aGuard(m_rMutex); + return new ::comphelper::OEnumerationByIndex(static_cast(this)); +} + +// XIndexAccess +sal_Int32 SAL_CALL OBookmarkContainer::getCount( ) +{ + MutexGuard aGuard(m_rMutex); + return m_aBookmarks.size(); +} + +Any SAL_CALL OBookmarkContainer::getByIndex( sal_Int32 _nIndex ) +{ + MutexGuard aGuard(m_rMutex); + + if ((_nIndex < 0) || (o3tl::make_unsigned(_nIndex) >= m_aBookmarksIndexed.size())) + throw IndexOutOfBoundsException(); + + return Any(m_aBookmarksIndexed[_nIndex]->second); +} + +Any SAL_CALL OBookmarkContainer::getByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_rMutex); + + if (!checkExistence(_rName)) + throw NoSuchElementException(); + + return Any(m_aBookmarks[_rName]); +} + +Sequence< OUString > SAL_CALL OBookmarkContainer::getElementNames( ) +{ + MutexGuard aGuard(m_rMutex); + + Sequence< OUString > aNames(m_aBookmarks.size()); + OUString* pNames = aNames.getArray(); + + for (auto const& bookmarkIndexed : m_aBookmarksIndexed) + { + *pNames = bookmarkIndexed->first; + ++pNames; + } + + return aNames; +} + +sal_Bool SAL_CALL OBookmarkContainer::hasByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_rMutex); + + return checkExistence(_rName); +} + +void OBookmarkContainer::implRemove(const OUString& _rName) +{ + MutexGuard aGuard(m_rMutex); + + // look for the name in the index access vector + MapString2String::const_iterator aMapPos = m_aBookmarks.end(); + auto aSearch = std::find_if(m_aBookmarksIndexed.begin(), m_aBookmarksIndexed.end(), + [&_rName](const MapString2String::iterator& rIter) { return rIter->first == _rName; }); + if (aSearch != m_aBookmarksIndexed.end()) + { + aMapPos = *aSearch; + m_aBookmarksIndexed.erase(aSearch); + } + + if (m_aBookmarks.end() == aMapPos) + { + OSL_FAIL("OBookmarkContainer::implRemove: inconsistence!"); + return; + } + + // remove the map entries + m_aBookmarks.erase(aMapPos); +} + +void OBookmarkContainer::implAppend(const OUString& _rName, const OUString& _rDocumentLocation) +{ + MutexGuard aGuard(m_rMutex); + + OSL_ENSURE(m_aBookmarks.find(_rName) == m_aBookmarks.end(),"Bookmark already known!"); + m_aBookmarksIndexed.push_back(m_aBookmarks.emplace( _rName,_rDocumentLocation).first); +} + +void OBookmarkContainer::implReplace(const OUString& _rName, const OUString& _rNewLink) +{ + MutexGuard aGuard(m_rMutex); + OSL_ENSURE(checkExistence(_rName), "OBookmarkContainer::implReplace : invalid name !"); + + m_aBookmarks[_rName] = _rNewLink; +} + +Reference< XInterface > SAL_CALL OBookmarkContainer::getParent( ) +{ + return m_rParent; +} + +void SAL_CALL OBookmarkContainer::setParent( const Reference< XInterface >& /*Parent*/ ) +{ + throw NoSupportException(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commandcontainer.cxx b/dbaccess/source/core/dataaccess/commandcontainer.cxx new file mode 100644 index 000000000..8afb81520 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commandcontainer.cxx @@ -0,0 +1,100 @@ +/* -*- 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 "commandcontainer.hxx" +#include "commanddefinition.hxx" + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ucb; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +// OCommandContainer + +OCommandContainer::OCommandContainer( const Reference< css::uno::XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTables + ) + :ODefinitionContainer(_xORB,_xParentContainer,_pImpl,!_bTables) + ,m_bTables(_bTables) +{ +} + +OCommandContainer::~OCommandContainer() +{ +} + +IMPLEMENT_FORWARD_XINTERFACE2( OCommandContainer,ODefinitionContainer,OCommandContainer_BASE) +css::uno::Sequence< css::uno::Type > OCommandContainer::getTypes() +{ + return ::comphelper::concatSequences( + ODefinitionContainer::getTypes( ), + OCommandContainer_BASE::getTypes( ) + ); +} + +css::uno::Sequence OCommandContainer::getImplementationId() +{ + return css::uno::Sequence(); +} + +Reference< XContent > OCommandContainer::createObject( const OUString& _rName) +{ + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + OSL_ENSURE( rDefinitions.find(_rName) != rDefinitions.end(), "OCommandContainer::createObject: Invalid entry in map!" ); + + const TContentPtr& pElementContent( rDefinitions.find( _rName )->second ); + if ( m_bTables ) + return new OComponentDefinition( *this, _rName, m_aContext, pElementContent, m_bTables ); + else + return static_cast< css::sdb::XQueryDefinition * > ( new OCommandDefinition( *this, _rName, m_aContext, pElementContent ) ); +} + +Reference< XInterface > SAL_CALL OCommandContainer::createInstanceWithArguments(const Sequence< Any >& /*aArguments*/ ) +{ + return createInstance( ); +} + +Reference< XInterface > SAL_CALL OCommandContainer::createInstance( ) +{ + if(m_bTables) + return css::sdb::TableDefinition::createDefault( m_aContext ); + else + return css::sdb::CommandDefinition::create( m_aContext ); +} + +OUString OCommandContainer::determineContentType() const +{ + return "application/vnd.org.openoffice.DatabaseCommandDefinitionContainer"; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commandcontainer.hxx b/dbaccess/source/core/dataaccess/commandcontainer.hxx new file mode 100644 index 000000000..3ea36f648 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commandcontainer.hxx @@ -0,0 +1,72 @@ +/* -*- 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 . + */ + +#pragma once + +#include + +#include + +#include + +namespace dbaccess +{ +// OCommandContainer + +typedef ::cppu::ImplHelper1 < css::lang::XSingleServiceFactory + > OCommandContainer_BASE; + +class OCommandContainer : public ODefinitionContainer + ,public OCommandContainer_BASE +{ + bool m_bTables; + +public: + /** constructs the container.
+ */ + OCommandContainer( + const css::uno::Reference< css::uno::XComponentContext >& _xORB + ,const css::uno::Reference< css::uno::XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + ,bool _bTables + ); + + DECLARE_XINTERFACE( ) + + virtual css::uno::Sequence SAL_CALL getTypes() override; + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +protected: + virtual ~OCommandContainer() override; + + // ODefinitionContainer + virtual css::uno::Reference< css::ucb::XContent > createObject(const OUString& _rName) override; + +protected: + // OContentHelper overridables + virtual OUString determineContentType() const override; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commanddefinition.cxx b/dbaccess/source/core/dataaccess/commanddefinition.cxx new file mode 100644 index 000000000..f04c47228 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commanddefinition.cxx @@ -0,0 +1,153 @@ +/* -*- 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 "commanddefinition.hxx" +#include +#include + +#include +#include + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +namespace dbaccess +{ + +void OCommandDefinition::registerProperties() +{ + OCommandDefinition_Impl& rCommandDefinition = dynamic_cast< OCommandDefinition_Impl& >( *m_pImpl ); + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, + &rCommandDefinition.m_sCommand, cppu::UnoType::get()); + + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, + &rCommandDefinition.m_bEscapeProcessing, cppu::UnoType::get()); + + registerProperty(PROPERTY_UPDATE_TABLENAME, PROPERTY_ID_UPDATE_TABLENAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateTableName, cppu::UnoType::get()); + + registerProperty(PROPERTY_UPDATE_SCHEMANAME, PROPERTY_ID_UPDATE_SCHEMANAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateSchemaName, cppu::UnoType::get()); + + registerProperty(PROPERTY_UPDATE_CATALOGNAME, PROPERTY_ID_UPDATE_CATALOGNAME, PropertyAttribute::BOUND, + &rCommandDefinition.m_sUpdateCatalogName, cppu::UnoType::get()); + registerProperty(PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, PropertyAttribute::BOUND, + &rCommandDefinition.m_aLayoutInformation, cppu::UnoType::get()); +} + +OCommandDefinition::OCommandDefinition(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _rxContainer + ,const TContentPtr& _pImpl) + :OComponentDefinition(_xORB,_rxContainer,_pImpl,false) +{ + registerProperties(); +} + +OCommandDefinition::~OCommandDefinition() +{ +} + +OCommandDefinition::OCommandDefinition( const Reference< XInterface >& _rxContainer + ,const OUString& _rElementName + ,const Reference< XComponentContext >& _xORB + ,const TContentPtr& _pImpl) + :OComponentDefinition(_rxContainer,_rElementName,_xORB,_pImpl,false) +{ + registerProperties(); +} + +css::uno::Sequence OCommandDefinition::getImplementationId() +{ + return css::uno::Sequence(); +} + +css::uno::Sequence< css::uno::Type > OCommandDefinition::getTypes() +{ + return ::comphelper::concatSequences( + OCommandDefinition_Base::getTypes( ), + OComponentDefinition::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE2( OCommandDefinition,OComponentDefinition,OCommandDefinition_Base) +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL OCommandDefinition::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& OCommandDefinition::getInfoHelper() +{ + return *OCommandDefinition_PROP::getArrayHelper(); +} +::cppu::IPropertyArrayHelper* OCommandDefinition::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +OUString SAL_CALL OCommandDefinition::getImplementationName() +{ + return "com.sun.star.comp.dba.OCommandDefinition"; +} + +css::uno::Sequence SAL_CALL OCommandDefinition::getSupportedServiceNames() +{ + return { + "com.sun.star.sdb.QueryDefinition", + "com.sun.star.sdb.CommandDefinition", + "com.sun.star.ucb.Content" + }; +} + +void SAL_CALL OCommandDefinition::rename( const OUString& newName ) +{ + try + { + sal_Int32 nHandle = PROPERTY_ID_NAME; + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + Any aOld(m_pImpl->m_aProps.aTitle); + aGuard.clear(); + Any aNew(newName); + fire(&nHandle, &aNew, &aOld, 1, true ); + + m_pImpl->m_aProps.aTitle = newName; + fire(&nHandle, &aNew, &aOld, 1, false ); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(newName,*this); + } +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_OCommandDefinition(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new dbaccess::OCommandDefinition( + context, nullptr, std::make_shared() )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/commanddefinition.hxx b/dbaccess/source/core/dataaccess/commanddefinition.hxx new file mode 100644 index 000000000..ed348a889 --- /dev/null +++ b/dbaccess/source/core/dataaccess/commanddefinition.hxx @@ -0,0 +1,125 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "ComponentDefinition.hxx" + +#include +#include +#include + +namespace dbaccess +{ + +// OCommandDefinition - a database "document" which describes a query + class OCommandDefinition_Impl : public OComponentDefinition_Impl + ,public OCommandBase + { + public: + }; + +typedef ::cppu::ImplHelper2 < css::sdbcx::XRename, + css::sdb::XQueryDefinition + > OCommandDefinition_Base; +class OCommandDefinition; +typedef ::comphelper::OPropertyArrayUsageHelper< OCommandDefinition > + OCommandDefinition_PROP; + +class OCommandDefinition : public OComponentDefinition + ,public OCommandDefinition_Base + ,public OCommandDefinition_PROP +{ +protected: + virtual ~OCommandDefinition() override; + +public: + OCommandDefinition(const css::uno::Reference< css::uno::XComponentContext >& , + const css::uno::Reference< css::uno::XInterface >& _xParentContainer, + const TContentPtr& _pImpl); + + OCommandDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer + ,const OUString& _rElementName + ,const css::uno::Reference< css::uno::XComponentContext >& + ,const TContentPtr& _pImpl + ); + + virtual css::uno::Sequence SAL_CALL getTypes() override; + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + +// css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // overrides to resolve ambiguity + virtual void SAL_CALL setPropertyValue(const OUString& p1, const css::uno::Any& p2) override + { OComponentDefinition::setPropertyValue(p1, p2); } + virtual css::uno::Any SAL_CALL getPropertyValue(const OUString& p1) override + { return OComponentDefinition::getPropertyValue(p1); } + virtual void SAL_CALL addPropertyChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { OComponentDefinition::addPropertyChangeListener(p1, p2); } + virtual void SAL_CALL removePropertyChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { OComponentDefinition::removePropertyChangeListener(p1, p2); } + virtual void SAL_CALL addVetoableChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { OComponentDefinition::addVetoableChangeListener(p1, p2); } + virtual void SAL_CALL removeVetoableChangeListener(const OUString& p1, const css::uno::Reference& p2) override + { OComponentDefinition::removeVetoableChangeListener(p1, p2); } + virtual css::uno::Reference SAL_CALL getIdentifier() override + { return OComponentDefinition::getIdentifier(); } + virtual OUString SAL_CALL getContentType() override + { return OComponentDefinition::getContentType(); } + virtual void SAL_CALL addContentEventListener(const css::uno::Reference& p1) override + { OComponentDefinition::addContentEventListener(p1); } + virtual void SAL_CALL removeContentEventListener(const css::uno::Reference& p1) override + { OComponentDefinition::removeContentEventListener(p1); } + virtual void SAL_CALL dispose() override + { OComponentDefinition::dispose(); } + virtual void SAL_CALL addEventListener(const css::uno::Reference& p1) override + { OComponentDefinition::addEventListener(p1); } + virtual void SAL_CALL removeEventListener(const css::uno::Reference& p1) override + { OComponentDefinition::removeEventListener(p1); } + + // OPropertySetHelper + virtual css::uno::Reference SAL_CALL getPropertySetInfo() override; + virtual cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + virtual cppu::IPropertyArrayHelper* createArrayHelper() const override; + + +private: + // helper + void registerProperties(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/connection.cxx b/dbaccess/source/core/dataaccess/connection.cxx new file mode 100644 index 000000000..37cf9e669 --- /dev/null +++ b/dbaccess/source/core/dataaccess/connection.cxx @@ -0,0 +1,808 @@ +/* -*- 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 + +#include + +#include "connection.hxx" +#include "datasource.hxx" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdb::application; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::reflection; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::graphic; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::dbtools; + +using ::com::sun::star::sdb::tools::XTableName; +using ::com::sun::star::sdb::tools::XObjectNames; +using ::com::sun::star::sdb::tools::XDataSourceMetaData; + +namespace dbaccess +{ + +// XServiceInfo +OUString OConnection::getImplementationName( ) +{ + return "com.sun.star.comp.dbaccess.Connection"; +} + +sal_Bool OConnection::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > OConnection::getSupportedServiceNames( ) +{ + Sequence< OUString > aSupported = OConnectionWrapper::getSupportedServiceNames(); + + if ( comphelper::findValue( aSupported, SERVICE_SDB_CONNECTION ) == -1 ) + { + sal_Int32 nLen = aSupported.getLength(); + aSupported.realloc( nLen + 1 ); + aSupported.getArray()[ nLen ] = SERVICE_SDB_CONNECTION; + } + + return aSupported; +} + +// XCloseable +void OConnection::close() +{ + // being closed is the same as being disposed + dispose(); +} + +sal_Bool OConnection::isClosed() +{ + MutexGuard aGuard(m_aMutex); + return !m_xMasterConnection.is(); +} + +// XConnection +Reference< XStatement > OConnection::createStatement() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference< XStatement > xStatement; + Reference< XStatement > xMasterStatement = m_xMasterConnection->createStatement(); + if ( xMasterStatement.is() ) + { + xStatement = new OStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +Reference< XPreparedStatement > OConnection::prepareStatement(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + // TODO convert the SQL to SQL the driver understands + Reference< XPreparedStatement > xStatement; + Reference< XPreparedStatement > xMasterStatement = m_xMasterConnection->prepareStatement(sql); + if ( xMasterStatement.is() ) + { + xStatement = new OPreparedStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +Reference< XPreparedStatement > OConnection::prepareCall(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference< XPreparedStatement > xStatement; + Reference< XPreparedStatement > xMasterStatement = m_xMasterConnection->prepareCall(sql); + if ( xMasterStatement.is() ) + { + xStatement = new OCallableStatement(this, xMasterStatement); + m_aStatements.emplace_back(xStatement); + } + return xStatement; +} + +OUString OConnection::nativeSQL(const OUString& sql) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->nativeSQL(sql); +} + +void OConnection::setAutoCommit(sal_Bool autoCommit) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setAutoCommit(autoCommit); +} + +sal_Bool OConnection::getAutoCommit() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getAutoCommit(); +} + +void OConnection::commit() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->commit(); +} + +void OConnection::rollback() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->rollback(); +} + +Reference< XDatabaseMetaData > OConnection::getMetaData() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getMetaData(); +} + +void OConnection::setReadOnly(sal_Bool readOnly) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setReadOnly(readOnly); +} + +sal_Bool OConnection::isReadOnly() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->isReadOnly(); +} + +void OConnection::setCatalog(const OUString& catalog) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setCatalog(catalog); +} + +OUString OConnection::getCatalog() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getCatalog(); +} + +void OConnection::setTransactionIsolation(sal_Int32 level) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setTransactionIsolation(level); +} + +sal_Int32 OConnection::getTransactionIsolation() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getTransactionIsolation(); +} + +Reference< XNameAccess > OConnection::getTypeMap() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xMasterConnection->getTypeMap(); +} + +void OConnection::setTypeMap(const Reference< XNameAccess > & typeMap) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_xMasterConnection->setTypeMap(typeMap); +} + +// OConnection + +OConnection::OConnection(ODatabaseSource& _rDB + , Reference< XConnection > const & _rxMaster + , const Reference< XComponentContext >& _rxORB) + :OSubComponent(m_aMutex, static_cast< OWeakObject* >(&_rDB)) + // as the queries reroute their refcounting to us, this m_aMutex is okey. If the queries + // container would do its own refcounting, it would have to acquire m_pMutex + // same for tables + ,m_aTableFilter(_rDB.m_pImpl->m_aTableFilter) + ,m_aTableTypeFilter(_rDB.m_pImpl->m_aTableTypeFilter) + ,m_aContext( _rxORB ) + ,m_xMasterConnection(_rxMaster) + ,m_aWarnings( Reference< XWarningsSupplier >( _rxMaster, UNO_QUERY ) ) + ,m_nInAppend(0) + ,m_bSupportsViews(false) + ,m_bSupportsUsers(false) + ,m_bSupportsGroups(false) +{ + osl_atomic_increment(&m_refCount); + + try + { + Reference< XProxyFactory > xProxyFactory = ProxyFactory::create( m_aContext ); + Reference xAgg = xProxyFactory->createProxy(_rxMaster); + setDelegation(xAgg,m_refCount); + OSL_ENSURE(m_xConnection.is(), "OConnection::OConnection : invalid master connection !"); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xTableUIProvider.set(m_xMasterConnection, css::uno::UNO_QUERY); + + try + { + m_xQueries = OQueryContainer::create(Reference< XNameContainer >(_rDB.getQueryDefinitions(), UNO_QUERY), this, _rxORB, &m_aWarnings).get(); + + bool bCase = true; + Reference xMeta; + try + { + xMeta = getMetaData(); + bCase = xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(); + } + catch(const SQLException&) + { + } + Reference< XNameContainer > xTableDefinitions(_rDB.getTables(),UNO_QUERY); + m_pTables.reset( new OTableContainer( *this, m_aMutex, this, bCase, xTableDefinitions, this, m_nInAppend ) ); + + // check if we support types + if ( xMeta.is() ) + { + Reference xRes = xMeta->getTableTypes(); + if(xRes.is()) + { + Reference xRow(xRes,UNO_QUERY); + while(xRes->next()) + { + OUString sValue = xRow->getString(1); + if( !xRow->wasNull() && sValue == "VIEW") + { + m_bSupportsViews = true; + break; + } + } + } + // some dbs don't support this type so we should ask if a XViewsSupplier is supported + if(!m_bSupportsViews) + { + Reference< XViewsSupplier > xMaster(getMasterTables(),UNO_QUERY); + + if (xMaster.is() && xMaster->getViews().is()) + m_bSupportsViews = true; + } + if(m_bSupportsViews) + { + m_pViews.reset( new OViewContainer(*this, m_aMutex, this, bCase, this, m_nInAppend) ); + m_pViews->addContainerListener(m_pTables.get()); + m_pTables->addContainerListener(m_pViews.get()); + } + m_bSupportsUsers = Reference< XUsersSupplier> (getMasterTables(),UNO_QUERY).is(); + m_bSupportsGroups = Reference< XGroupsSupplier> (getMasterTables(),UNO_QUERY).is(); + + impl_checkTableQueryNames_nothrow(); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + osl_atomic_decrement( &m_refCount ); +} + +OConnection::~OConnection() +{ +} + +// XWarningsSupplier +Any SAL_CALL OConnection::getWarnings() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_aWarnings.getWarnings(); +} + +void SAL_CALL OConnection::clearWarnings( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + m_aWarnings.clearWarnings(); +} + +namespace +{ + struct CompareTypeByName + { + bool operator() ( const Type& _rLHS, const Type& _rRHS ) const + { + return _rLHS.getTypeName() < _rRHS.getTypeName(); + } + }; + typedef std::set< Type, CompareTypeByName > TypeBag; + + void lcl_copyTypes( TypeBag& _out_rTypes, const Sequence< Type >& _rTypes ) + { + std::copy( _rTypes.begin(), _rTypes.end(), + std::insert_iterator< TypeBag >( _out_rTypes, _out_rTypes.begin() ) ); + } +} + +// css::lang::XTypeProvider +Sequence< Type > OConnection::getTypes() +{ + TypeBag aNormalizedTypes; + + lcl_copyTypes( aNormalizedTypes, OSubComponent::getTypes() ); + lcl_copyTypes( aNormalizedTypes, OConnection_Base::getTypes() ); + lcl_copyTypes( aNormalizedTypes, ::connectivity::OConnectionWrapper::getTypes() ); + + if ( !m_bSupportsViews ) + aNormalizedTypes.erase( cppu::UnoType::get() ); + if ( !m_bSupportsUsers ) + aNormalizedTypes.erase( cppu::UnoType::get() ); + if ( !m_bSupportsGroups ) + aNormalizedTypes.erase( cppu::UnoType::get() ); + + return comphelper::containerToSequence(aNormalizedTypes); +} + +Sequence< sal_Int8 > OConnection::getImplementationId() +{ + return css::uno::Sequence(); +} + +// css::uno::XInterface +Any OConnection::queryInterface( const Type & rType ) +{ + if ( !m_bSupportsViews && rType.equals( cppu::UnoType::get() ) ) + return Any(); + else if ( !m_bSupportsUsers && rType.equals( cppu::UnoType::get() ) ) + return Any(); + else if ( !m_bSupportsGroups && rType.equals( cppu::UnoType::get() ) ) + return Any(); + Any aReturn = OSubComponent::queryInterface( rType ); + if (!aReturn.hasValue()) + { + aReturn = OConnection_Base::queryInterface( rType ); + if (!aReturn.hasValue()) + aReturn = OConnectionWrapper::queryInterface( rType ); + } + return aReturn; +} + +void OConnection::acquire() noexcept +{ + // include this one when you want to see who calls it (call graph) + OSubComponent::acquire(); +} + +void OConnection::release() noexcept +{ + // include this one when you want to see who calls it (call graph) + OSubComponent::release(); +} + +// OSubComponent +void OConnection::disposing() +{ + MutexGuard aGuard(m_aMutex); + + OSubComponent::disposing(); + OConnectionWrapper::disposing(); + + for (auto const& statement : m_aStatements) + { + Reference xComp(statement.get(),UNO_QUERY); + ::comphelper::disposeComponent(xComp); + } + m_aStatements.clear(); + m_xMasterTables = nullptr; + + if(m_pTables) + m_pTables->dispose(); + if(m_pViews) + m_pViews->dispose(); + + ::comphelper::disposeComponent(m_xQueries); + + for (auto const& composer : m_aComposers) + { + Reference xComp(composer.get(),UNO_QUERY); + ::comphelper::disposeComponent(xComp); + } + + m_aComposers.clear(); + + try + { + if (m_xMasterConnection.is()) + m_xMasterConnection->close(); + } + catch(const Exception&) + { + } + m_xMasterConnection = nullptr; +} + +// XChild +Reference< XInterface > OConnection::getParent() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + return m_xParent; +} + +void OConnection::setParent(const Reference< XInterface > & /*Parent*/) +{ + throw NoSupportException(); +} + +// XSQLQueryComposerFactory +Reference< XSQLQueryComposer > OConnection::createQueryComposer() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + // Reference< XNumberFormatsSupplier > xSupplier = pParent->getNumberFormatsSupplier(); + Reference< XSQLQueryComposer > xComposer( new OQueryComposer( this ) ); + m_aComposers.emplace_back(xComposer); + return xComposer; +} + +void OConnection::impl_fillTableFilter() +{ + Reference xProp(getParent(),UNO_QUERY); + if ( xProp.is() ) + { + xProp->getPropertyValue(PROPERTY_TABLEFILTER) >>= m_aTableFilter; + xProp->getPropertyValue(PROPERTY_TABLETYPEFILTER) >>= m_aTableTypeFilter; + } +} + +void OConnection::refresh(const Reference< XNameAccess >& _rToBeRefreshed) +{ + if ( _rToBeRefreshed == Reference< XNameAccess >(m_pTables.get()) ) + { + if (m_pTables && !m_pTables->isInitialized()) + { + impl_fillTableFilter(); + // check if our "master connection" can supply tables + getMasterTables(); + + if (m_xMasterTables.is() && m_xMasterTables->getTables().is()) + { // yes -> wrap them + m_pTables->construct(m_xMasterTables->getTables(),m_aTableFilter, m_aTableTypeFilter); + } + else + { // no -> use an own container + m_pTables->construct(m_aTableFilter, m_aTableTypeFilter); + } + } + } + else if ( _rToBeRefreshed == Reference< XNameAccess >(m_pViews.get()) ) + { + if (m_pViews && !m_pViews->isInitialized()) + { + impl_fillTableFilter(); + // check if our "master connection" can supply tables + Reference< XViewsSupplier > xMaster(getMasterTables(),UNO_QUERY); + + if (xMaster.is() && xMaster->getViews().is()) + m_pViews->construct(xMaster->getViews(),m_aTableFilter, m_aTableTypeFilter); + else + m_pViews->construct(m_aTableFilter, m_aTableTypeFilter); + } + } +} + +// XTablesSupplier +Reference< XNameAccess > OConnection::getTables() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + refresh(m_pTables.get()); + + return m_pTables.get(); +} + +Reference< XNameAccess > SAL_CALL OConnection::getViews( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + refresh(m_pViews.get()); + + return m_pViews.get(); +} + +// XQueriesSupplier +Reference< XNameAccess > OConnection::getQueries() +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + return m_xQueries; +} + +// css::sdb::XCommandPreparation +Reference< XPreparedStatement > SAL_CALL OConnection::prepareCommand( const OUString& command, sal_Int32 commandType ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + OUString aStatement; + switch (commandType) + { + case CommandType::TABLE: + { + aStatement = "SELECT * FROM "; + + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents( getMetaData(), command, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation ); + aStatement += ::dbtools::composeTableNameForSelect( this, sCatalog, sSchema, sTable ); + } + break; + case CommandType::QUERY: + if ( m_xQueries->hasByName(command) ) + { + Reference< XPropertySet > xQuery(m_xQueries->getByName(command),UNO_QUERY); + xQuery->getPropertyValue(PROPERTY_COMMAND) >>= aStatement; + } + break; + default: + aStatement = command; + } + // TODO EscapeProcessing + return prepareStatement(aStatement); +} + +Reference< XInterface > SAL_CALL OConnection::createInstance( const OUString& _sServiceSpecifier ) +{ + Reference< XServiceInfo > xRet; + if ( SERVICE_NAME_SINGLESELECTQUERYCOMPOSER == _sServiceSpecifier || _sServiceSpecifier == "com.sun.star.sdb.SingleSelectQueryAnalyzer" ) + { + xRet = new OSingleSelectQueryComposer( getTables(),this, m_aContext ); + m_aComposers.emplace_back(xRet); + } + else + { + if ( !_sServiceSpecifier.isEmpty() ) + { + TSupportServices::const_iterator aFind = m_aSupportServices.find(_sServiceSpecifier); + if ( aFind == m_aSupportServices.end() ) + { + Reference xMy(this); + Sequence aArgs{ Any(NamedValue("ActiveConnection",Any(xMy))) }; + aFind = m_aSupportServices.emplace( + _sServiceSpecifier, + m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(_sServiceSpecifier, aArgs, m_aContext) + ).first; + } + return aFind->second; + } + } + return Reference(xRet, UNO_QUERY); +} + +Reference< XInterface > SAL_CALL OConnection::createInstanceWithArguments( const OUString& _sServiceSpecifier, const Sequence< Any >& /*Arguments*/ ) +{ + return createInstance(_sServiceSpecifier); +} + +Sequence< OUString > SAL_CALL OConnection::getAvailableServiceNames( ) +{ + Sequence< OUString > aRet { SERVICE_NAME_SINGLESELECTQUERYCOMPOSER }; + return aRet; +} + +Reference< XTablesSupplier > const & OConnection::getMasterTables() +{ +// check if out "master connection" can supply tables + if(!m_xMasterTables.is()) + { + try + { + Reference xMeta = getMetaData(); + if ( xMeta.is() ) + m_xMasterTables = ::dbtools::getDataDefinitionByURLAndConnection( xMeta->getURL(), m_xMasterConnection, m_aContext ); + } + catch(const SQLException&) + { + } + } + return m_xMasterTables; +} + +// XUsersSupplier +Reference< XNameAccess > SAL_CALL OConnection::getUsers( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + + Reference xUsr(getMasterTables(),UNO_QUERY); + return xUsr.is() ? xUsr->getUsers() : Reference< XNameAccess >(); +} + +// XGroupsSupplier +Reference< XNameAccess > SAL_CALL OConnection::getGroups( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + Reference xGrp(getMasterTables(),UNO_QUERY); + return xGrp.is() ? xGrp->getGroups() : Reference< XNameAccess >(); +} + +void OConnection::impl_loadConnectionTools_throw() +{ + m_xConnectionTools = css::sdb::tools::ConnectionTools::createWithConnection( m_aContext, this ); +} + +Reference< XTableName > SAL_CALL OConnection::createTableName( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->createTableName(); +} + +Reference< XObjectNames > SAL_CALL OConnection::getObjectNames( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getObjectNames(); +} + +Reference< XDataSourceMetaData > SAL_CALL OConnection::getDataSourceMetaData( ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getDataSourceMetaData(); +} + +Reference< css::container::XNameAccess > SAL_CALL OConnection::getFieldsByCommandDescriptor( ::sal_Int32 commandType, const OUString& command, css::uno::Reference< css::lang::XComponent >& keepFieldsAlive ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getFieldsByCommandDescriptor(commandType,command,keepFieldsAlive); +} + +Reference< XSingleSelectQueryComposer > SAL_CALL OConnection::getComposer( ::sal_Int32 commandType, const OUString& command ) +{ + MutexGuard aGuard(m_aMutex); + checkDisposed(); + impl_loadConnectionTools_throw(); + + return m_xConnectionTools->getComposer(commandType,command); +} + +void OConnection::impl_checkTableQueryNames_nothrow() +{ + DatabaseMetaData aMeta( static_cast< XConnection* >( this ) ); + if ( !aMeta.supportsSubqueriesInFrom() ) + // nothing to do + return; + + try + { + Reference< XNameAccess > xTables( getTables() ); + const Sequence< OUString > aTableNames( xTables->getElementNames() ); + std::set< OUString > aSortedTableNames( aTableNames.begin(), aTableNames.end() ); + + Reference< XNameAccess > xQueries( getQueries() ); + const Sequence< OUString > aQueryNames( xQueries->getElementNames() ); + + for ( auto const & queryName : aQueryNames ) + { + if ( aSortedTableNames.find( queryName ) != aSortedTableNames.end() ) + { + OUString sConflictWarning( DBA_RES( RID_STR_CONFLICTING_NAMES ) ); + m_aWarnings.appendWarning( sConflictWarning, "01SB0", *this ); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< XGraphic > SAL_CALL OConnection::getTableIcon( const OUString& TableName, ::sal_Int32 ColorMode ) +{ + Reference< XGraphic > xReturn; + + // ask our aggregate + if ( m_xTableUIProvider.is() ) + xReturn = m_xTableUIProvider->getTableIcon( TableName, ColorMode ); + + // ask ourself + // well, we don't have own functionality here ... + // In the future, we might decide to delegate the complete handling to this interface. + // In this case, we would need to load the icon here. + + return xReturn; +} + +Reference< XInterface > SAL_CALL OConnection::getTableEditor( const Reference< XDatabaseDocumentUI >& DocumentUI, const OUString& TableName ) +{ + Reference< XInterface > xReturn; + + // ask our aggregate + if ( m_xTableUIProvider.is() ) + xReturn = m_xTableUIProvider->getTableEditor( DocumentUI, TableName ); + + // ask ourself + // well, we don't have own functionality here ... + // In the future, we might decide to delegate the complete handling to this interface. + // In this case, we would need to instantiate a css.sdb.TableDesign here. + + return xReturn; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/connection.hxx b/dbaccess/source/core/dataaccess/connection.hxx new file mode 100644 index 000000000..4ecfa7d70 --- /dev/null +++ b/dbaccess/source/core/dataaccess/connection.hxx @@ -0,0 +1,229 @@ +/* -*- 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 . + */ +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace dbaccess +{ + +typedef cppu::ImplHelper13 < css::container::XChild + , css::sdbcx::XTablesSupplier + , css::sdbcx::XViewsSupplier + , css::sdbc::XConnection + , css::sdbc::XWarningsSupplier + , css::sdb::XQueriesSupplier + , css::sdb::XSQLQueryComposerFactory + , css::sdb::XCommandPreparation + , css::lang::XMultiServiceFactory + , css::sdbcx::XUsersSupplier + , css::sdbcx::XGroupsSupplier + , css::sdb::tools::XConnectionTools + , css::sdb::application::XTableUIProvider + > OConnection_Base; + +class ODatabaseSource; +// OConnection +class OConnection final :public ::cppu::BaseMutex + ,public OSubComponent + ,public ::connectivity::OConnectionWrapper + ,public OConnection_Base + ,public IRefreshListener +{ + css::uno::Reference< css::sdbcx::XTablesSupplier > + m_xMasterTables; // just to avoid the recreation of the catalog + connectivity::OWeakRefArray m_aStatements; + css::uno::Reference< css::container::XNameAccess > + m_xQueries; + connectivity::OWeakRefArray m_aComposers; + + // the filter as set on the parent data link at construction of the connection + css::uno::Sequence< OUString > m_aTableFilter; + css::uno::Sequence< OUString > m_aTableTypeFilter; + css::uno::Reference< css::uno::XComponentContext > m_aContext; + css::uno::Reference< css::sdbc::XConnection > m_xMasterConnection; + css::uno::Reference< css::sdb::tools::XConnectionTools > m_xConnectionTools; + css::uno::Reference< css::sdb::application::XTableUIProvider > m_xTableUIProvider; + + // defines the helper services for example to query the command of a view + // @ see com.sun.star.sdb.tools.XViewAccess + typedef std::map< OUString, css::uno::Reference< css::uno::XInterface> > TSupportServices; + TSupportServices m_aSupportServices; + + std::unique_ptr m_pTables; + std::unique_ptr m_pViews; + ::dbtools::WarningsContainer m_aWarnings; + std::atomic m_nInAppend; + bool m_bSupportsViews; // true when the getTableTypes return "VIEW" as type + bool m_bSupportsUsers; + bool m_bSupportsGroups; + + virtual ~OConnection() override; +public: + OConnection(ODatabaseSource& _rDB + ,css::uno::Reference< css::sdbc::XConnection > const & _rxMaster + ,const css::uno::Reference< css::uno::XComponentContext >& _rxORB); + +// css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + +// css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + +// OComponentHelper + virtual void SAL_CALL disposing() override; + +// css::container::XChild + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + +// css::sdbcx::XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; +// css::sdbcx::XViewsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getViews( ) override; + +// css::sdb::XQueriesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getQueries( ) override; + +// css::sdb::XSQLQueryComposerFactory + virtual css::uno::Reference< css::sdb::XSQLQueryComposer > SAL_CALL createQueryComposer( ) override; + +// css::sdb::XCommandPreparation + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCommand( const OUString& command, sal_Int32 commandType ) override; + +// css::sdbc::XWarningsSupplier + virtual css::uno::Any SAL_CALL getWarnings( ) override; + virtual void SAL_CALL clearWarnings( ) override; + +// css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +// XConnection + virtual css::uno::Reference< css::sdbc::XStatement > SAL_CALL createStatement( ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareStatement( const OUString& sql ) override; + virtual css::uno::Reference< css::sdbc::XPreparedStatement > SAL_CALL prepareCall( const OUString& sql ) override; + virtual OUString SAL_CALL nativeSQL( const OUString& sql ) override; + virtual void SAL_CALL setAutoCommit( sal_Bool autoCommit ) override; + virtual sal_Bool SAL_CALL getAutoCommit( ) override; + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL rollback( ) override; + virtual sal_Bool SAL_CALL isClosed( ) override; + virtual css::uno::Reference< css::sdbc::XDatabaseMetaData > SAL_CALL getMetaData( ) override; + virtual void SAL_CALL setReadOnly( sal_Bool readOnly ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL setCatalog( const OUString& catalog ) override; + virtual OUString SAL_CALL getCatalog( ) override; + virtual void SAL_CALL setTransactionIsolation( sal_Int32 level ) override; + virtual sal_Int32 SAL_CALL getTransactionIsolation( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTypeMap( ) override; + virtual void SAL_CALL setTypeMap( const css::uno::Reference< css::container::XNameAccess >& typeMap ) override; + +// css::sdbc::XCloseable + virtual void SAL_CALL close( ) override; + + // XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XUsersSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getUsers( ) override; + // XGroupsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getGroups( ) override; + + // XConnectionTools + virtual css::uno::Reference< css::sdb::tools::XTableName > SAL_CALL createTableName( ) override; + virtual css::uno::Reference< css::sdb::tools::XObjectNames > SAL_CALL getObjectNames( ) override; + virtual css::uno::Reference< css::sdb::tools::XDataSourceMetaData > SAL_CALL getDataSourceMetaData( ) override; + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getFieldsByCommandDescriptor( ::sal_Int32 commandType, const OUString& command, css::uno::Reference< css::lang::XComponent >& keepFieldsAlive ) override; + virtual css::uno::Reference< css::sdb::XSingleSelectQueryComposer > SAL_CALL getComposer( ::sal_Int32 commandType, const OUString& command ) override; + + // XTableUIProvider + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL getTableIcon( const OUString& TableName, ::sal_Int32 ColorMode ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getTableEditor( const css::uno::Reference< css::sdb::application::XDatabaseDocumentUI >& DocumentUI, const OUString& TableName ) override; + + // IRefreshListener + virtual void refresh(const css::uno::Reference< css::container::XNameAccess >& _rToBeRefreshed) override; + +private: + /// @throws css::lang::DisposedException + void checkDisposed() + { + if ( rBHelper.bDisposed || !m_xConnection.is() ) + throw css::lang::DisposedException(); + } + + css::uno::Reference< css::sdbcx::XTablesSupplier > const & getMasterTables(); + + /** checks whether or not there are naming conflicts between tables and queries + */ + void impl_checkTableQueryNames_nothrow(); + + /** loads the XConnectionTools implementation which we forward the respective functionality to + + @throws css::uno::RuntimeException + if the implementation cannot be loaded + + @postcond + m_xConnectionTools is nol + */ + void impl_loadConnectionTools_throw(); + + /** reads the table filter and table type filter from the datasource + */ + void impl_fillTableFilter(); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx b/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx new file mode 100644 index 000000000..ef5239a67 --- /dev/null +++ b/dbaccess/source/core/dataaccess/dataaccessdescriptor.cxx @@ -0,0 +1,229 @@ +/* -*- 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::lang::XServiceInfo; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::beans::Property; + using ::com::sun::star::sdbc::XConnection; + using ::com::sun::star::sdbc::XResultSet; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::PropertyValue; + + namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute; + namespace CommandType = ::com::sun::star::sdb::CommandType; + + // DataAccessDescriptor + typedef ::comphelper::OMutexAndBroadcastHelper DataAccessDescriptor_MutexBase; + + typedef ::cppu::WeakImplHelper< XServiceInfo + > DataAccessDescriptor_TypeBase; + + typedef ::comphelper::OPropertyContainer DataAccessDescriptor_PropertyBase; + + class DataAccessDescriptor :public DataAccessDescriptor_MutexBase + ,public DataAccessDescriptor_TypeBase + ,public DataAccessDescriptor_PropertyBase + ,public ::comphelper::OPropertyArrayUsageHelper< DataAccessDescriptor > + { + public: + DataAccessDescriptor(); + + // UNO + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + protected: + virtual ~DataAccessDescriptor() override; + + protected: + // XPropertySet + virtual Reference< XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + private: + // + OUString m_sDataSourceName; + OUString m_sDatabaseLocation; + OUString m_sConnectionResource; + Sequence< PropertyValue > m_aConnectionInfo; + Reference< XConnection > m_xActiveConnection; + OUString m_sCommand; + sal_Int32 m_nCommandType; + OUString m_sFilter; + OUString m_sOrder; + OUString m_sHavingClause; + OUString m_sGroupBy; + bool m_bEscapeProcessing; + Reference< XResultSet > m_xResultSet; + Sequence< Any > m_aSelection; + bool m_bBookmarkSelection; + OUString m_sColumnName; + Reference< XPropertySet > m_xColumn; + // + }; + + DataAccessDescriptor::DataAccessDescriptor() + :DataAccessDescriptor_PropertyBase( m_aBHelper ) + ,m_nCommandType( CommandType::COMMAND ) + ,m_bEscapeProcessing( true ) + ,m_bBookmarkSelection( true ) + { + registerProperty(PROPERTY_DATASOURCENAME, PROPERTY_ID_DATASOURCENAME, PropertyAttribute::BOUND, &m_sDataSourceName , cppu::UnoType::get()); + registerProperty(PROPERTY_DATABASE_LOCATION, PROPERTY_ID_DATABASE_LOCATION, PropertyAttribute::BOUND, &m_sDatabaseLocation , cppu::UnoType::get()); + registerProperty(PROPERTY_CONNECTION_RESOURCE, PROPERTY_ID_CONNECTION_RESOURCE, PropertyAttribute::BOUND, &m_sConnectionResource , cppu::UnoType::get()); + registerProperty(PROPERTY_CONNECTION_INFO, PROPERTY_ID_CONNECTION_INFO, PropertyAttribute::BOUND, &m_aConnectionInfo , cppu::UnoType::get()); + registerProperty(PROPERTY_ACTIVE_CONNECTION, PROPERTY_ID_ACTIVE_CONNECTION, PropertyAttribute::BOUND, &m_xActiveConnection , cppu::UnoType::get()); + registerProperty(PROPERTY_COMMAND, PROPERTY_ID_COMMAND, PropertyAttribute::BOUND, &m_sCommand , cppu::UnoType::get()); + registerProperty(PROPERTY_COMMAND_TYPE, PROPERTY_ID_COMMAND_TYPE, PropertyAttribute::BOUND, &m_nCommandType , cppu::UnoType::get()); + registerProperty(PROPERTY_FILTER, PROPERTY_ID_FILTER, PropertyAttribute::BOUND, &m_sFilter , cppu::UnoType::get()); + registerProperty(PROPERTY_ORDER, PROPERTY_ID_ORDER, PropertyAttribute::BOUND, &m_sOrder , cppu::UnoType::get()); + registerProperty(PROPERTY_HAVING_CLAUSE, PROPERTY_ID_HAVING_CLAUSE, PropertyAttribute::BOUND, &m_sHavingClause , cppu::UnoType::get()); + registerProperty(PROPERTY_GROUP_BY, PROPERTY_ID_GROUP_BY, PropertyAttribute::BOUND, &m_sGroupBy , cppu::UnoType::get()); + registerProperty(PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::BOUND, &m_bEscapeProcessing , cppu::UnoType::get()); + registerProperty(PROPERTY_RESULT_SET, PROPERTY_ID_RESULT_SET, PropertyAttribute::BOUND, &m_xResultSet , cppu::UnoType::get()); + registerProperty(PROPERTY_SELECTION, PROPERTY_ID_SELECTION, PropertyAttribute::BOUND, &m_aSelection , cppu::UnoType::get()); + registerProperty(PROPERTY_BOOKMARK_SELECTION, PROPERTY_ID_BOOKMARK_SELECTION, PropertyAttribute::BOUND, &m_bBookmarkSelection , cppu::UnoType::get()); + registerProperty(PROPERTY_COLUMN_NAME, PROPERTY_ID_COLUMN_NAME, PropertyAttribute::BOUND, &m_sColumnName , cppu::UnoType::get()); + registerProperty(PROPERTY_COLUMN, PROPERTY_ID_COLUMN, PropertyAttribute::BOUND, &m_xColumn , cppu::UnoType::get()); + } + + DataAccessDescriptor::~DataAccessDescriptor() + { + } + + IMPLEMENT_FORWARD_XINTERFACE2( DataAccessDescriptor, DataAccessDescriptor_TypeBase, DataAccessDescriptor_PropertyBase ); + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( DataAccessDescriptor, DataAccessDescriptor_TypeBase, DataAccessDescriptor_PropertyBase ); + + OUString SAL_CALL DataAccessDescriptor::getImplementationName() + { + return "com.sun.star.comp.dba.DataAccessDescriptor"; + } + + sal_Bool SAL_CALL DataAccessDescriptor::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > SAL_CALL DataAccessDescriptor::getSupportedServiceNames( ) + { + return { "com.sun.star.sdb.DataAccessDescriptor" }; + } + + Reference< XPropertySetInfo > SAL_CALL DataAccessDescriptor::getPropertySetInfo() + { + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; + } + + ::cppu::IPropertyArrayHelper& DataAccessDescriptor::getInfoHelper() + { + return *getArrayHelper(); + } + + ::cppu::IPropertyArrayHelper* DataAccessDescriptor::createArrayHelper( ) const + { + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); + } + + // DataAccessDescriptorFactory + class DataAccessDescriptorFactory: public ::cppu::WeakImplHelper + { + public: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XDataAccessDescriptorFactory + virtual Reference< XPropertySet > SAL_CALL createDataAccessDescriptor( ) override; + + DataAccessDescriptorFactory(); + }; + + DataAccessDescriptorFactory::DataAccessDescriptorFactory() + { + } + + OUString SAL_CALL DataAccessDescriptorFactory::getImplementationName() + { + return "com.sun.star.comp.dba.DataAccessDescriptorFactory"; + } + + sal_Bool SAL_CALL DataAccessDescriptorFactory::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + Sequence< OUString > SAL_CALL DataAccessDescriptorFactory::getSupportedServiceNames() + { + return { "com.sun.star.sdb.DataAccessDescriptorFactory" }; + } + + Reference< XPropertySet > SAL_CALL DataAccessDescriptorFactory::createDataAccessDescriptor( ) + { + return new DataAccessDescriptor(); + } + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_dba_DataAccessDescriptorFactory( + css::uno::XComponentContext *, + css::uno::Sequence const &) +{ + return cppu::acquire(new DataAccessDescriptorFactory()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasecontext.cxx b/dbaccess/source/core/dataaccess/databasecontext.cxx new file mode 100644 index 000000000..5afdadb67 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasecontext.cxx @@ -0,0 +1,753 @@ +/* -*- 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 + +#include +#include +#include +#include +#include "databaseregistrations.hxx" +#include "datasource.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::osl; +using namespace ::utl; + +using ::com::sun::star::task::InteractionClassification_ERROR; +using ::com::sun::star::ucb::IOErrorCode_NO_FILE; +using ::com::sun::star::ucb::InteractiveIOException; +using ::com::sun::star::ucb::IOErrorCode_NOT_EXISTING; +using ::com::sun::star::ucb::IOErrorCode_NOT_EXISTING_PATH; + +namespace dbaccess +{ + + typedef ::cppu::WeakImplHelper< XTerminateListener + > DatabaseDocumentLoader_Base; + class DatabaseDocumentLoader : public DatabaseDocumentLoader_Base + { + private: + Reference< XDesktop2 > m_xDesktop; + std::vector< const ODatabaseModelImpl* > m_aDatabaseDocuments; + + public: + explicit DatabaseDocumentLoader( const Reference & rxContext); + + void append(const ODatabaseModelImpl& _rModelImpl ) + { + m_aDatabaseDocuments.emplace_back(&_rModelImpl); + } + + void remove(const ODatabaseModelImpl& _rModelImpl) + { + m_aDatabaseDocuments.erase(std::find(m_aDatabaseDocuments.begin(), m_aDatabaseDocuments.end(), &_rModelImpl)); + } + + + private: + // XTerminateListener + virtual void SAL_CALL queryTermination( const lang::EventObject& Event ) override; + virtual void SAL_CALL notifyTermination( const lang::EventObject& Event ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + }; + + DatabaseDocumentLoader::DatabaseDocumentLoader( const Reference & rxContext ) + { + try + { + m_xDesktop.set( Desktop::create(rxContext) ); + m_xDesktop->addTerminateListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void SAL_CALL DatabaseDocumentLoader::queryTermination( const lang::EventObject& /*Event*/ ) + { + std::vector< const ODatabaseModelImpl* > aCpy(m_aDatabaseDocuments); + for( const auto& pCopy : aCpy ) + { + try + { + const Reference< XModel2 > xMod( pCopy->getModel_noCreate(), + UNO_QUERY_THROW ); + if( !xMod->getControllers()->hasMoreElements() ) + { + Reference< util::XCloseable > xClose( xMod, + UNO_QUERY_THROW ); + xClose->close( false ); + } + } + catch( const CloseVetoException& ) + { + throw TerminationVetoException(); + } + } + } + + void SAL_CALL DatabaseDocumentLoader::notifyTermination( const lang::EventObject& /*Event*/ ) + { + } + + void SAL_CALL DatabaseDocumentLoader::disposing( const lang::EventObject& /*Source*/ ) + { + } + +// ODatabaseContext + +ODatabaseContext::ODatabaseContext( const Reference< XComponentContext >& _rxContext ) + :DatabaseAccessContext_Base(m_aMutex) + ,m_aContext( _rxContext ) + ,m_aContainerListeners(m_aMutex) +{ + m_xDatabaseDocumentLoader = new DatabaseDocumentLoader( _rxContext ); + +#if HAVE_FEATURE_SCRIPTING + ::basic::BasicManagerRepository::registerCreationListener( *this ); +#endif + + osl_atomic_increment( &m_refCount ); + { + m_xDBRegistrationAggregate.set( createDataSourceRegistrations( m_aContext ), UNO_SET_THROW ); + m_xDatabaseRegistrations.set( m_xDBRegistrationAggregate, UNO_QUERY_THROW ); + + m_xDBRegistrationAggregate->setDelegator( *this ); + } + osl_atomic_decrement( &m_refCount ); +} + +ODatabaseContext::~ODatabaseContext() +{ +#if HAVE_FEATURE_SCRIPTING + ::basic::BasicManagerRepository::revokeCreationListener( *this ); +#endif + + m_xDatabaseDocumentLoader.clear(); + m_xDBRegistrationAggregate->setDelegator( nullptr ); + m_xDBRegistrationAggregate.clear(); + m_xDatabaseRegistrations.clear(); +} + +// XServiceInfo +OUString ODatabaseContext::getImplementationName( ) +{ + return "com.sun.star.comp.dba.ODatabaseContext"; +} + +sal_Bool ODatabaseContext::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Sequence< OUString > ODatabaseContext::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DatabaseContext" }; +} + +Reference< XInterface > ODatabaseContext::impl_createNewDataSource() +{ + ::rtl::Reference pImpl( new ODatabaseModelImpl( m_aContext, *this ) ); + Reference< XDataSource > xDataSource( pImpl->getOrCreateDataSource() ); + + return xDataSource; +} + +Reference< XInterface > SAL_CALL ODatabaseContext::createInstance( ) +{ + // for convenience of the API user, we ensure the document is fully initialized (effectively: XLoadable::initNew + // has been called at the DatabaseDocument). + return impl_createNewDataSource(); +} + +Reference< XInterface > SAL_CALL ODatabaseContext::createInstanceWithArguments( const Sequence< Any >& _rArguments ) +{ + ::comphelper::NamedValueCollection aArgs( _rArguments ); + OUString sURL = aArgs.getOrDefault( INFO_POOLURL, OUString() ); + + Reference< XInterface > xDataSource; + if ( !sURL.isEmpty() ) + xDataSource = getObject( sURL ); + + if ( !xDataSource.is() ) + xDataSource = impl_createNewDataSource(); + + return xDataSource; +} + +// DatabaseAccessContext_Base +void ODatabaseContext::disposing() +{ + // notify our listener + css::lang::EventObject aDisposeEvent(static_cast< XContainer* >(this)); + m_aContainerListeners.disposeAndClear(aDisposeEvent); + + // dispose the data sources + // disposing seems to remove elements, so work on copy for valid iterators + ObjectCache objCopy; + objCopy.swap(m_aDatabaseObjects); + for (auto const& elem : objCopy) + { + rtl::Reference< ODatabaseModelImpl > obj(elem.second); + // make sure obj is acquired and does not delete itself from within + // dispose() + obj->dispose(); + } +} + +// XNamingService +Reference< XInterface > ODatabaseContext::getRegisteredObject(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + OUString sURL( getDatabaseLocation( _rName ) ); + + if ( sURL.isEmpty() ) + // there is a registration for this name, but no URL + throw IllegalArgumentException(); + + // check if URL is already loaded + Reference< XInterface > xExistent = getObject( sURL ); + if ( xExistent.is() ) + return xExistent; + + return loadObjectFromURL( _rName, sURL ); +} + +Reference< XInterface > ODatabaseContext::loadObjectFromURL(const OUString& _rName,const OUString& _sURL) +{ + INetURLObject aURL( _sURL ); + + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + throw NoSuchElementException( _rName, *this ); + + bool bEmbeddedDataSource = aURL.isSchemeEqualTo(INetProtocol::VndSunStarPkg); + try + { + if (!bEmbeddedDataSource) + { + ::ucbhelper::Content aContent( _sURL, nullptr, comphelper::getProcessComponentContext() ); + if ( !aContent.isDocument() ) + throw InteractiveIOException( + _sURL, *this, InteractionClassification_ERROR, IOErrorCode_NO_FILE + ); + } + } + catch ( const InteractiveIOException& e ) + { + if ( ( e.Code == IOErrorCode_NO_FILE ) + || ( e.Code == IOErrorCode_NOT_EXISTING ) + || ( e.Code == IOErrorCode_NOT_EXISTING_PATH ) + ) + { + // #i40463# #i39187# + OUString sErrorMessage( DBA_RES( RID_STR_FILE_DOES_NOT_EXIST ) ); + ::svt::OFileNotation aTransformer( _sURL ); + + SQLException aError; + aError.Message = sErrorMessage.replaceAll( "$file$", aTransformer.get( ::svt::OFileNotation::N_SYSTEM ) ); + + throw WrappedTargetException( _sURL, *this, Any( aError ) ); + } + throw WrappedTargetException( _sURL, *this, ::cppu::getCaughtException() ); + } + catch( const Exception& ) + { + throw WrappedTargetException( _sURL, *this, ::cppu::getCaughtException() ); + } + + OSL_ENSURE( m_aDatabaseObjects.find( _sURL ) == m_aDatabaseObjects.end(), + "ODatabaseContext::loadObjectFromURL: not intended for already-cached objects!" ); + + ::rtl::Reference< ODatabaseModelImpl > pModelImpl; + { + pModelImpl.set( new ODatabaseModelImpl( _rName, m_aContext, *this ) ); + + Reference< XModel > xModel( pModelImpl->createNewModel_deliverOwnership(), UNO_SET_THROW ); + Reference< XLoadable > xLoad( xModel, UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aArgs; + aArgs.put( "URL", _sURL ); + aArgs.put( "MacroExecutionMode", MacroExecMode::USE_CONFIG ); + aArgs.put( "InteractionHandler", task::InteractionHandler::createWithParent(m_aContext, nullptr) ); + if (bEmbeddedDataSource) + { + // In this case the host contains the real path, and the path is the embedded stream name. + auto const uri = css::uri::UriReferenceFactory::create(m_aContext)->parse(_sURL); + if (uri.is() && uri->isAbsolute() + && uri->hasAuthority() && !uri->hasQuery() && !uri->hasFragment()) + { + auto const auth = uri->getAuthority(); + auto const decAuth = rtl::Uri::decode( + auth, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8); + if (auth.isEmpty() == decAuth.isEmpty()) { + // Decoding of auth to UTF-8 succeeded: + OUString sBaseURI = decAuth + uri->getPath(); + aArgs.put("BaseURI", sBaseURI); + } else { + SAL_WARN( + "dbaccess.core", + "<" << _sURL << "> cannot be parse as vnd.sun.star.pkg URL"); + } + } else { + SAL_WARN( + "dbaccess.core", "<" << _sURL << "> cannot be parse as vnd.sun.star.pkg URL"); + } + } + + Sequence< PropertyValue > aResource( aArgs.getPropertyValues() ); + xLoad->load( aResource ); + xModel->attachResource( _sURL, aResource ); + + ::utl::CloseableComponent aEnsureClose( xModel ); + } + + setTransientProperties( _sURL, *pModelImpl ); + + return pModelImpl->getOrCreateDataSource(); +} + +void ODatabaseContext::appendAtTerminateListener(const ODatabaseModelImpl& _rDataSourceModel) +{ + m_xDatabaseDocumentLoader->append(_rDataSourceModel); +} + +void ODatabaseContext::removeFromTerminateListener(const ODatabaseModelImpl& _rDataSourceModel) +{ + m_xDatabaseDocumentLoader->remove(_rDataSourceModel); +} + +void ODatabaseContext::setTransientProperties(const OUString& _sURL, ODatabaseModelImpl& _rDataSourceModel ) +{ + if ( m_aDatasourceProperties.end() == m_aDatasourceProperties.find(_sURL) ) + return; + try + { + OUString sAuthFailedPassword; + Reference< XPropertySet > xDSProps( _rDataSourceModel.getOrCreateDataSource(), UNO_QUERY_THROW ); + const Sequence< PropertyValue >& rSessionPersistentProps = m_aDatasourceProperties[_sURL]; + for ( auto const & prop : rSessionPersistentProps ) + { + if ( prop.Name == "AuthFailedPassword" ) + { + OSL_VERIFY( prop.Value >>= sAuthFailedPassword ); + } + else + { + xDSProps->setPropertyValue( prop.Name, prop.Value ); + } + } + + _rDataSourceModel.m_sFailedPassword = sAuthFailedPassword; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODatabaseContext::registerObject(const OUString& _rName, const Reference< XInterface > & _rxObject) +{ + if ( _rName.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + Reference< XDocumentDataSource > xDocDataSource( _rxObject, UNO_QUERY ); + Reference< XModel > xModel( xDocDataSource.is() ? xDocDataSource->getDatabaseDocument() : Reference< XOfficeDatabaseDocument >(), UNO_QUERY ); + if ( !xModel.is() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + OUString sURL = xModel->getURL(); + if ( sURL.isEmpty() ) + throw IllegalArgumentException( DBA_RES( RID_STR_DATASOURCE_NOT_STORED ), *this, 2 ); + + { // avoid deadlocks: lock m_aMutex after checking arguments + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + registerDatabaseLocation( _rName, sURL ); + + ODatabaseSource::setName( xDocDataSource, _rName, ODatabaseSource::DBContextAccess() ); + } + + // notify our container listeners + ContainerEvent aEvent(static_cast(this), Any(_rName), Any(_rxObject), Any()); + m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvent ); +} + +void ODatabaseContext::storeTransientProperties( ODatabaseModelImpl& _rModelImpl) +{ + Reference< XPropertySet > xSource( _rModelImpl.getOrCreateDataSource(), UNO_QUERY ); + ::comphelper::NamedValueCollection aRememberProps; + + try + { + // get the info about the properties, check which ones are transient and not readonly + Reference< XPropertySetInfo > xSetInfo; + if (xSource.is()) + xSetInfo = xSource->getPropertySetInfo(); + Sequence< Property > aProperties; + if (xSetInfo.is()) + aProperties = xSetInfo->getProperties(); + + for ( const Property& rProperty : std::as_const(aProperties) ) + { + if ( ( ( rProperty.Attributes & PropertyAttribute::TRANSIENT) != 0 ) + && ( ( rProperty.Attributes & PropertyAttribute::READONLY) == 0 ) + ) + { + // found such a property + aRememberProps.put( rProperty.Name, xSource->getPropertyValue( rProperty.Name ) ); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // additionally, remember the "failed password", which is not available as property + // #i86178# + aRememberProps.put( "AuthFailedPassword", _rModelImpl.m_sFailedPassword ); + + OUString sDocumentURL( _rModelImpl.getURL() ); + if ( m_aDatabaseObjects.find( sDocumentURL ) != m_aDatabaseObjects.end() ) + { + m_aDatasourceProperties[ sDocumentURL ] = aRememberProps.getPropertyValues(); + } + else if ( m_aDatabaseObjects.find( _rModelImpl.m_sName ) != m_aDatabaseObjects.end() ) + { + OSL_FAIL( "ODatabaseContext::storeTransientProperties: a database document register by name? This shouldn't happen anymore!" ); + // all the code should have been changed so that registration is by URL only + m_aDatasourceProperties[ _rModelImpl.m_sName ] = aRememberProps.getPropertyValues(); + } + else + { + OSL_ENSURE( sDocumentURL.isEmpty() && _rModelImpl.m_sName.isEmpty() , + "ODatabaseContext::storeTransientProperties: a non-empty data source which I do not know?!" ); + } +} + +void SAL_CALL ODatabaseContext::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL ODatabaseContext::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + m_aContainerListeners.removeInterface(_rxListener); +} + +void ODatabaseContext::revokeObject(const OUString& _rName) +{ + ClearableMutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + OUString sURL = getDatabaseLocation( _rName ); + + revokeDatabaseLocation( _rName ); + // will throw if something goes wrong + + if ( m_aDatabaseObjects.find( _rName ) != m_aDatabaseObjects.end() ) + { + m_aDatasourceProperties[ sURL ] = m_aDatasourceProperties[ _rName ]; + } + + // check if URL is already loaded + ObjectCache::const_iterator aExistent = m_aDatabaseObjects.find( sURL ); + if ( aExistent != m_aDatabaseObjects.end() ) + m_aDatabaseObjects.erase( aExistent ); + + // notify our container listeners + ContainerEvent aEvent( *this, Any( _rName ), Any(), Any() ); + aGuard.clear(); + m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvent ); +} + +sal_Bool SAL_CALL ODatabaseContext::hasRegisteredDatabase( const OUString& Name ) +{ + return m_xDatabaseRegistrations->hasRegisteredDatabase( Name ); +} + +Sequence< OUString > SAL_CALL ODatabaseContext::getRegistrationNames() +{ + return m_xDatabaseRegistrations->getRegistrationNames(); +} + +OUString SAL_CALL ODatabaseContext::getDatabaseLocation( const OUString& Name ) +{ + return m_xDatabaseRegistrations->getDatabaseLocation( Name ); +} + +void SAL_CALL ODatabaseContext::registerDatabaseLocation( const OUString& Name, const OUString& Location ) +{ + m_xDatabaseRegistrations->registerDatabaseLocation( Name, Location ); +} + +void SAL_CALL ODatabaseContext::revokeDatabaseLocation( const OUString& Name ) +{ + m_xDatabaseRegistrations->revokeDatabaseLocation( Name ); +} + +void SAL_CALL ODatabaseContext::changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) +{ + m_xDatabaseRegistrations->changeDatabaseLocation( Name, NewLocation ); +} + +sal_Bool SAL_CALL ODatabaseContext::isDatabaseRegistrationReadOnly( const OUString& Name ) +{ + return m_xDatabaseRegistrations->isDatabaseRegistrationReadOnly( Name ); +} + +void SAL_CALL ODatabaseContext::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) +{ + m_xDatabaseRegistrations->addDatabaseRegistrationsListener( Listener ); +} + +void SAL_CALL ODatabaseContext::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) +{ + m_xDatabaseRegistrations->removeDatabaseRegistrationsListener( Listener ); +} + +// css::container::XElementAccess +Type ODatabaseContext::getElementType( ) +{ + return cppu::UnoType::get(); +} + +sal_Bool ODatabaseContext::hasElements() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return getElementNames().hasElements(); +} + +// css::container::XEnumerationAccess +Reference< css::container::XEnumeration > ODatabaseContext::createEnumeration() +{ + MutexGuard aGuard(m_aMutex); + return new ::comphelper::OEnumerationByName(static_cast(this)); +} + +// css::container::XNameAccess +Any ODatabaseContext::getByName(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + if ( _rName.isEmpty() ) + throw NoSuchElementException(_rName, *this); + + try + { + Reference< XInterface > xExistent = getObject( _rName ); + if ( xExistent.is() ) + return Any( xExistent ); + + // see whether this is a registered name + OUString sURL; + if ( hasRegisteredDatabase( _rName ) ) + { + sURL = getDatabaseLocation( _rName ); + // is the object cached under its URL? + xExistent = getObject( sURL ); + } + else + // interpret the name as URL + sURL = _rName; + + if ( !xExistent.is() ) + // try to load this as URL + xExistent = loadObjectFromURL( _rName, sURL ); + return Any( xExistent ); + } + catch (const NoSuchElementException&) + { // let these exceptions through + throw; + } + catch (const WrappedTargetException&) + { // let these exceptions through + throw; + } + catch (const RuntimeException&) + { // let these exceptions through + throw; + } + catch (const Exception&) + { // exceptions other than the specified ones -> wrap + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException(_rName, *this, aError ); + } +} + +Sequence< OUString > ODatabaseContext::getElementNames() +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return getRegistrationNames(); +} + +sal_Bool ODatabaseContext::hasByName(const OUString& _rName) +{ + MutexGuard aGuard(m_aMutex); + ::connectivity::checkDisposed(DatabaseAccessContext_Base::rBHelper.bDisposed); + + return hasRegisteredDatabase( _rName ); +} + +Reference< XInterface > ODatabaseContext::getObject( const OUString& _rURL ) +{ + ObjectCache::const_iterator aFind = m_aDatabaseObjects.find( _rURL ); + Reference< XInterface > xExistent; + if ( aFind != m_aDatabaseObjects.end() ) + xExistent = aFind->second->getOrCreateDataSource(); + return xExistent; +} + +void ODatabaseContext::registerDatabaseDocument( ODatabaseModelImpl& _rModelImpl ) +{ + OUString sURL( _rModelImpl.getURL() ); + SAL_INFO("dbaccess.core", "DatabaseContext: registering " << sURL); + if ( m_aDatabaseObjects.find( sURL ) == m_aDatabaseObjects.end() ) + { + m_aDatabaseObjects[ sURL ] = &_rModelImpl; + setTransientProperties( sURL, _rModelImpl ); + } + else + OSL_FAIL( "ODatabaseContext::registerDatabaseDocument: already have an object registered for this URL!" ); +} + +void ODatabaseContext::revokeDatabaseDocument( const ODatabaseModelImpl& _rModelImpl ) +{ + const OUString& sURL( _rModelImpl.getURL() ); + SAL_INFO("dbaccess.core", "DatabaseContext: deregistering " << sURL); + m_aDatabaseObjects.erase( sURL ); +} + +void ODatabaseContext::databaseDocumentURLChange( const OUString& _rOldURL, const OUString& _rNewURL ) +{ + SAL_INFO("dbaccess.core", "DatabaseContext: changing registrations from " << _rOldURL << + " to " << _rNewURL); + ObjectCache::const_iterator oldPos = m_aDatabaseObjects.find( _rOldURL ); + ENSURE_OR_THROW( oldPos != m_aDatabaseObjects.end(), "illegal old database document URL" ); + ObjectCache::const_iterator newPos = m_aDatabaseObjects.find( _rNewURL ); + ENSURE_OR_THROW( newPos == m_aDatabaseObjects.end(), "illegal new database document URL" ); + + m_aDatabaseObjects[ _rNewURL ] = oldPos->second; + m_aDatabaseObjects.erase( oldPos ); +} + +sal_Int64 SAL_CALL ODatabaseContext::getSomething( const Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +const Sequence< sal_Int8 > & ODatabaseContext::getUnoTunnelId() +{ + static const comphelper::UnoIdInit implId; + return implId.getSeq(); +} + +void ODatabaseContext::onBasicManagerCreated( const Reference< XModel >& _rxForDocument, BasicManager& _rBasicManager ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) _rxForDocument; + (void) _rBasicManager; +#else + // if it's a database document ... + Reference< XOfficeDatabaseDocument > xDatabaseDocument( _rxForDocument, UNO_QUERY ); + // ... or a sub document of a database document ... + if ( !xDatabaseDocument.is() ) + { + Reference< XChild > xDocAsChild( _rxForDocument, UNO_QUERY ); + if ( xDocAsChild.is() ) + xDatabaseDocument.set( xDocAsChild->getParent(), UNO_QUERY ); + } + + // ... whose BasicManager has just been created, then add the global DatabaseDocument variable to its scope. + if ( xDatabaseDocument.is() ) + _rBasicManager.SetGlobalUNOConstant( "ThisDatabaseDocument", Any( xDatabaseDocument ) ); +#endif +} + +} // namespace dbaccess + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseContext_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence const& ) +{ + return cppu::acquire(new dbaccess::ODatabaseContext(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasedocument.cxx b/dbaccess/source/core/dataaccess/databasedocument.cxx new file mode 100644 index 000000000..419d02d2d --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasedocument.cxx @@ -0,0 +1,2211 @@ +/* -*- 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 +#include +#include "databasedocument.hxx" +#include "documenteventexecutor.hxx" +#include +#include "documentcontainer.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::script::provider; +using namespace ::com::sun::star::ui; +using namespace ::cppu; + +namespace dbaccess +{ + +// ViewMonitor + +bool ViewMonitor::onControllerConnected( const Reference< XController >& _rxController ) +{ + bool bFirstControllerEver = !m_bEverHadController; + m_bEverHadController = true; + + m_xLastConnectedController = _rxController; + m_bLastIsFirstEverController = bFirstControllerEver; + + return bFirstControllerEver; +} + +bool ViewMonitor::onSetCurrentController( const Reference< XController >& _rxController ) +{ + // we interpret this as "loading the document (including UI) is finished", + // if and only if this is the controller which was last connected, and it was the + // first controller ever connected + bool bLoadFinished = ( _rxController == m_xLastConnectedController ) && m_bLastIsFirstEverController; + + // notify the respective events + if ( bLoadFinished ) + m_rEventNotifier.notifyDocumentEventAsync( m_bIsNewDocument ? "OnNew" : "OnLoad" ); + + return bLoadFinished; +} + + +ODatabaseDocument::ODatabaseDocument(const ::rtl::Reference& _pImpl ) + :ModelDependentComponent( _pImpl ) + ,ODatabaseDocument_OfficeDocument( getMutex() ) + ,m_aModifyListeners( getMutex() ) + ,m_aCloseListener( getMutex() ) + ,m_aStorageListeners( getMutex() ) + ,m_pEventContainer( new DocumentEvents( *this, getMutex(), _pImpl->getDocumentEvents() ) ) + ,m_aEventNotifier( *this, getMutex() ) + ,m_aViewMonitor( m_aEventNotifier ) + ,m_eInitState( NotInitialized ) + ,m_bClosing( false ) + ,m_bAllowDocumentScripting( false ) + ,m_bHasBeenRecovered( false ) + ,m_bEmbedded(false) +{ + osl_atomic_increment( &m_refCount ); + { + impl_reparent_nothrow( m_xForms ); + impl_reparent_nothrow( m_xReports ); + impl_reparent_nothrow( m_pImpl->m_xTableDefinitions ); + impl_reparent_nothrow( m_pImpl->m_xCommandDefinitions ); + + m_pEventExecutor = new DocumentEventExecutor( m_pImpl->m_aContext, this ); + } + osl_atomic_decrement( &m_refCount ); + + // if there previously was a document instance for the same Impl which was already initialized, + // then consider ourself initialized, too. + // #i94840# + if ( !m_pImpl->hadInitializedDocument() ) + return; + + // Note we set our init-state to "Initializing", not "Initialized". We're created from inside the ModelImpl, + // which is expected to call attachResource in case there was a previous incarnation of the document, + // so we can properly finish our initialization then. + impl_setInitializing(); + + if ( !m_pImpl->getURL().isEmpty() ) + { + // if the previous incarnation of the DatabaseDocument already had a URL, then creating this incarnation + // here is effectively loading the document. + // #i105505# + m_aViewMonitor.onLoadedDocument(); + } +} + +ODatabaseDocument::~ODatabaseDocument() +{ + if ( !ODatabaseDocument_OfficeDocument::rBHelper.bInDispose && !ODatabaseDocument_OfficeDocument::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +Any SAL_CALL ODatabaseDocument::queryInterface( const Type& _rType ) +{ + // strip XEmbeddedScripts and XScriptInvocationContext if we have any form/report + // which already contains macros. In this case, the database document itself is not + // allowed to contain macros, too. + if ( !m_bAllowDocumentScripting + && ( _rType.equals( cppu::UnoType::get() ) + || _rType.equals( cppu::UnoType::get() ) + ) + ) + return Any(); + + Any aReturn = ODatabaseDocument_OfficeDocument::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = ODatabaseDocument_Title::queryInterface(_rType); + return aReturn; +} + +void SAL_CALL ODatabaseDocument::acquire( ) noexcept +{ + ODatabaseDocument_OfficeDocument::acquire(); +} + +void SAL_CALL ODatabaseDocument::release( ) noexcept +{ + ODatabaseDocument_OfficeDocument::release(); +} + +Sequence< Type > SAL_CALL ODatabaseDocument::getTypes( ) +{ + Sequence< Type > aTypes = ::comphelper::concatSequences( + ODatabaseDocument_OfficeDocument::getTypes(), + ODatabaseDocument_Title::getTypes() + ); + + // strip XEmbeddedScripts and XScriptInvocationContext if we have any form/report + // which already contains macros. In this case, the database document itself is not + // allowed to contain macros, too. + if ( !m_bAllowDocumentScripting ) + { + auto [begin, end] = asNonConstRange(aTypes); + auto newEnd = std::remove_if( begin, end, + [](const Type& t) + { return t == cppu::UnoType::get() || + t == cppu::UnoType::get();} ); + aTypes.realloc( std::distance(begin, newEnd) ); + } + + return aTypes; +} + +Sequence< sal_Int8 > SAL_CALL ODatabaseDocument::getImplementationId( ) +{ + return css::uno::Sequence(); +} + +// local functions +namespace +{ + Reference< XStatusIndicator > lcl_extractStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments ) + { + Reference< XStatusIndicator > xStatusIndicator; + return _rArguments.getOrDefault( "StatusIndicator", xStatusIndicator ); + } + + void lcl_triggerStatusIndicator_throw( const ::comphelper::NamedValueCollection& _rArguments, DocumentGuard& _rGuard, const bool _bStart ) + { + Reference< XStatusIndicator > xStatusIndicator( lcl_extractStatusIndicator( _rArguments ) ); + if ( !xStatusIndicator.is() ) + return; + + _rGuard.clear(); + try + { + if ( _bStart ) + xStatusIndicator->start( OUString(), sal_Int32(1000000) ); + else + xStatusIndicator->end(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + _rGuard.reset(); + // note that |reset| can throw a DisposedException + } + + void lcl_extractStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments, Sequence< Any >& _rCallArgs ) + { + Reference< XStatusIndicator > xStatusIndicator( lcl_extractStatusIndicator( _rArguments ) ); + if ( !xStatusIndicator.is() ) + return; + + sal_Int32 nLength = _rCallArgs.getLength(); + _rCallArgs.realloc( nLength + 1 ); + _rCallArgs.getArray()[ nLength ] <<= xStatusIndicator; + } + + void lcl_extractAndStartStatusIndicator( const ::comphelper::NamedValueCollection& _rArguments, Reference< XStatusIndicator >& _rxStatusIndicator, + Sequence< Any >& _rCallArgs ) + { + _rxStatusIndicator = lcl_extractStatusIndicator( _rArguments ); + if ( !_rxStatusIndicator.is() ) + return; + + try + { + _rxStatusIndicator->start( OUString(), sal_Int32(1000000) ); + + sal_Int32 nLength = _rCallArgs.getLength(); + _rCallArgs.realloc( nLength + 1 ); + _rCallArgs.getArray()[ nLength ] <<= _rxStatusIndicator; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + Sequence< PropertyValue > lcl_appendFileNameToDescriptor( const ::comphelper::NamedValueCollection& _rDescriptor, const OUString& _rURL ) + { + ::comphelper::NamedValueCollection aMutableDescriptor( _rDescriptor ); + if ( !_rURL.isEmpty() ) + { + aMutableDescriptor.put( "FileName", _rURL ); + aMutableDescriptor.put( "URL", _rURL ); + } + return aMutableDescriptor.getPropertyValues(); + } +} + +constexpr OUStringLiteral sPictures = u"Pictures"; + +// base documents seem to have a different behaviour to other documents, the +// root storage contents at least seem to be re-used over different saves, thus if there is a +// top level Picture directory it is never cleared. +// If we delete the 'Pictures' directory then the dialog library storage which does store +// any embed images will not work properly. ( this is due to the fact it will +// try to load the dialog which will try and access the embed images, if those images are not cached in +// memory it will try to read them from the Picture directory which is now gone, so... we have to use this +// inglorious hack below which basically will +// +// a) create a temp storage +// +// b) introspect any dialogs for any embed graphics and grab the associate URL(s) +// +// c) populate the temp storage with the associated embed images ( will be stored in a 'Pictures' folder ) +// +// d) delete the 'Picture' element from the root storage +// +// e) copy the Pictures element of the temp storage to the root storage +// +// this assumes that we don't use the Pictures folder in the root of the base +// document for anything, I believe this is a valid assumption ( as much as +// I could check anyway ) + +/// @throws RuntimeException +static void lcl_uglyHackToStoreDialogeEmbedImages( const Reference< XStorageBasedLibraryContainer >& xDlgCont, const Reference< XStorage >& xStorage, const Reference< XModel >& rxModel, const Reference& rxContext ) +{ + const Sequence< OUString > sLibraries = xDlgCont->getElementNames(); + Reference< XStorage > xTmpPic = xStorage->openStorageElement( "tempPictures", ElementModes::READWRITE ); + + std::vector> vxGraphicList; + for ( OUString const & sLibrary : sLibraries ) + { + xDlgCont->loadLibrary( sLibrary ); + Reference< XNameContainer > xLib; + xDlgCont->getByName( sLibrary ) >>= xLib; + if ( xLib.is() ) + { + Sequence< OUString > sDialogs = xLib->getElementNames(); + sal_Int32 nDialogs( sDialogs.getLength() ); + for ( sal_Int32 j=0; j < nDialogs; ++j ) + { + Reference < awt::XDialogProvider > xDlgPrv = awt::DialogProvider::createWithModel(rxContext, rxModel); + OUString sDialogUrl = + "vnd.sun.star.script:" + sLibrary + "." + sDialogs[j] + "?location=document"; + + Reference< css::awt::XControl > xDialog( xDlgPrv->createDialog( sDialogUrl ), UNO_QUERY ); + Reference< XInterface > xModel( xDialog->getModel() ); + vcl::graphic::SearchForGraphics(xModel, vxGraphicList); + } + } + } + // if we have any image urls, make sure we copy the associated images into tempPictures + if (!vxGraphicList.empty()) + { + // Export the images to the storage + uno::Reference xGraphicStorageHandler; + xGraphicStorageHandler.set(GraphicStorageHandler::createWithStorage(rxContext, xTmpPic)); + if (xGraphicStorageHandler.is()) + { + for (uno::Reference const & rxGraphic : vxGraphicList) + { + xGraphicStorageHandler->saveGraphic(rxGraphic); + } + } + // delete old 'Pictures' storage and copy the contents of tempPictures into xStorage + xStorage->removeElement( sPictures ); + xTmpPic->copyElementTo( sPictures, xStorage, sPictures ); + } + else + { + // clean up an existing Pictures dir + if ( xStorage->isStorageElement( sPictures ) ) + xStorage->removeElement( sPictures ); + } +} + +void ODatabaseDocument::impl_setInitialized() +{ + m_eInitState = Initialized; + + // start event notifications + m_aEventNotifier.onDocumentInitialized(); +} + +void ODatabaseDocument::impl_reset_nothrow() +{ + try + { + m_pImpl->clearConnections(); + m_pImpl->disposeStorages(); + m_pImpl->resetRootStorage(); + + clearObjectContainer( m_xForms ); + clearObjectContainer( m_xReports ); + clearObjectContainer( m_pImpl->m_xTableDefinitions ); + clearObjectContainer( m_pImpl->m_xCommandDefinitions ); + + m_eInitState = NotInitialized; + + m_pImpl->reset(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_pImpl->m_bDocumentReadOnly = false; +} + +namespace +{ + /** property map for import/export info set */ + comphelper::PropertyMapEntry const aExportInfoMap[] = + { + { OUString("BaseURI"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("UsePrettyPrinting"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("TargetStorage"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("StreamRelPath"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + }; +} + +void ODatabaseDocument::impl_import_nolck_throw( const Reference< XComponentContext >& _rContext, const Reference< XInterface >& _rxTargetComponent, + const ::comphelper::NamedValueCollection& _rResource ) +{ + Sequence< Any > aFilterCreationArgs; + Reference< XStatusIndicator > xStatusIndicator; + lcl_extractAndStartStatusIndicator( _rResource, xStatusIndicator, aFilterCreationArgs ); + + uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) ); + OUString sBaseURI = _rResource.getOrDefault("BaseURI", OUString()); + if (sBaseURI.isEmpty()) + sBaseURI = _rResource.getOrDefault("URL",OUString()); + assert(!sBaseURI.isEmpty()); // needed for relative URLs + xInfoSet->setPropertyValue("BaseURI", uno::Any(sBaseURI)); + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + + const sal_Int32 nCount = aFilterCreationArgs.getLength(); + aFilterCreationArgs.realloc(nCount + 1); + aFilterCreationArgs.getArray()[nCount] <<= xInfoSet; + + Reference< XImporter > xImporter( + _rContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.comp.sdb.DBFilter", aFilterCreationArgs, _rContext), + UNO_QUERY_THROW ); + + Reference< XComponent > xComponent( _rxTargetComponent, UNO_QUERY_THROW ); + xImporter->setTargetDocument( xComponent ); + + Reference< XFilter > xFilter( xImporter, UNO_QUERY_THROW ); + Sequence< PropertyValue > aFilterArgs( ODatabaseModelImpl::stripLoadArguments( _rResource ).getPropertyValues() ); + xFilter->filter( aFilterArgs ); + + if ( xStatusIndicator.is() ) + xStatusIndicator->end(); +} + +void SAL_CALL ODatabaseDocument::initNew( ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + impl_reset_nothrow(); + + impl_setInitializing(); + + // create a temporary storage + Reference< XStorage > xTempStor( ::comphelper::OStorageHelper::GetTemporaryStorage( m_pImpl->m_aContext ) ); + + // store therein + impl_storeToStorage_throw( xTempStor, Sequence< PropertyValue >(), aGuard ); + + // let the impl know we're now based on this storage + m_pImpl->switchToStorage( xTempStor ); + + // for the newly created document, allow document-wide scripting + m_bAllowDocumentScripting = true; + + impl_setInitialized(); + + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + + impl_setModified_nothrow( false, aGuard ); + // <- SYNCHRONIZED + + m_aEventNotifier.notifyDocumentEvent( "OnCreate" ); + + impl_notifyStorageChange_nolck_nothrow( xTempStor ); +} + +void SAL_CALL ODatabaseDocument::load( const Sequence< PropertyValue >& Arguments ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + impl_reset_nothrow(); + + ::comphelper::NamedValueCollection aResource( Arguments ); + if ( aResource.has( "FileName" ) && !aResource.has( "URL" ) ) + // FileName is the compatibility name for URL, so we might have clients passing + // a FileName only. However, some of our code works with the URL only, so ensure + // we have one. + aResource.put( "URL", aResource.get( "FileName" ) ); + if ( aResource.has( "URL" ) && !aResource.has( "FileName" ) ) + // similar ... just in case there is legacy code which expects a FileName only + aResource.put( "FileName", aResource.get( "URL" ) ); + + // now that somebody (perhaps) told us a macro execution mode, remember it as + // ImposedMacroExecMode + m_pImpl->setImposedMacroExecMode( + aResource.getOrDefault( "MacroExecutionMode", m_pImpl->getImposedMacroExecMode() ) ); + + impl_setInitializing(); + try + { + aGuard.clear(); + impl_import_nolck_throw( m_pImpl->m_aContext, *this, aResource ); + aGuard.reset(); + } + catch( const Exception& ) + { + impl_reset_nothrow(); + throw; + } + // tell our view monitor that the document has been loaded - this way it will fire the proper + // event (OnLoad instead of OnCreate) later on + m_aViewMonitor.onLoadedDocument(); + + // note that we do *not* call impl_setInitialized() here: The initialization is only complete + // when the XModel::attachResource has been called, not sooner. + // however, in case of embedding, XModel::attachResource is already called. + if (m_bEmbedded) + impl_setInitialized(); + + impl_setModified_nothrow( false, aGuard ); + // <- SYNCHRONIZED +} + +namespace +{ + bool lcl_hasAnyModifiedSubComponent_throw( const Reference< XController >& i_rController ) + { + Reference< css::sdb::application::XDatabaseDocumentUI > xDatabaseUI( i_rController, UNO_QUERY_THROW ); + + const Sequence< Reference< XComponent > > aComponents( xDatabaseUI->getSubComponents() ); + + bool isAnyModified = false; + for ( auto const & xComponent : aComponents ) + { + Reference< XModifiable > xModify( xComponent, UNO_QUERY ); + if ( xModify.is() ) + { + isAnyModified = xModify->isModified(); + continue; + } + + // TODO: clarify: anything else to care for? Both the sub components with and without model + // should support the XModifiable interface, so I think nothing more is needed here. + OSL_FAIL( "lcl_hasAnyModifiedSubComponent_throw: anything left to do here?" ); + } + + return isAnyModified; + } +} + +sal_Bool SAL_CALL ODatabaseDocument::wasModifiedSinceLastSave() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + // The implementation here is somewhat sloppy, in that it returns whether *any* part of the whole + // database document, including opened sub components, is modified. This is more than what is requested: + // We need to return if the doc itself, or any of the opened sub components, has been modified + // since the last call to any of the save* methods, or since the document has been loaded/created. + // However, the API definition explicitly allows to be that sloppy ... + + if ( isModified() ) + return true; + + // auto recovery is an "UI feature", it is to restore the UI the user knows. Thus, + // we ask our connected controllers, not simply our existing form/report definitions. + // (There is some information which even cannot be obtained without asking the controller. + // For instance, newly created, but not yet saved, forms/reports are accessible via the + // controller only, but not via the model.) + + try + { + for (auto const& controller : m_aControllers) + { + if ( lcl_hasAnyModifiedSubComponent_throw(controller) ) + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return false; +} + +void SAL_CALL ODatabaseDocument::storeToRecoveryFile( const OUString& i_TargetLocation, const Sequence< PropertyValue >& i_MediaDescriptor ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + ModifyLock aLock( *this ); + + try + { + // create a storage for the target location + Reference< XStorage > xTargetStorage( impl_createStorageFor_throw( i_TargetLocation ) ); + + // first store the document as a whole into this storage + impl_storeToStorage_throw( xTargetStorage, i_MediaDescriptor, aGuard ); + + // save the sub components which need saving + DatabaseDocumentRecovery aDocRecovery( m_pImpl->m_aContext); + aDocRecovery.saveModifiedSubComponents( xTargetStorage, m_aControllers ); + + // commit the root storage + tools::stor::commitStorageIfWriteable( xTargetStorage ); + } + catch( const IOException& ) + { + throw; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const WrappedTargetException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException( OUString(), *this, aError ); + } +} + +void SAL_CALL ODatabaseDocument::recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const Sequence< PropertyValue >& i_MediaDescriptor ) +{ + try + { + DocumentGuard aGuard( *this, DocumentGuard::InitMethod ); + + if ( i_SourceLocation.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + + // load the document itself, by simply delegating to our "load" method + + // our load implementation expects the SalvagedFile and URL to be in the media descriptor + ::comphelper::NamedValueCollection aMediaDescriptor( i_MediaDescriptor ); + aMediaDescriptor.put( "SalvagedFile", i_SalvagedFile ); + aMediaDescriptor.put( "URL", i_SourceLocation ); + + aGuard.clear(); // (load has an own guarding scheme) + load( aMediaDescriptor.getPropertyValues() ); + aGuard.reset(); + + // Without a controller, we are unable to recover the sub components, as they're always tied to a controller. + // So, everything else is done when the first controller is connected. + m_bHasBeenRecovered = true; + + // tell the impl that we've been loaded from the given location + m_pImpl->setDocFileLocation( i_SourceLocation ); + + // by definition (of XDocumentRecovery), we're responsible for delivering a fully-initialized document, + // which includes an attachResource call. + const OUString sLogicalDocumentURL( i_SalvagedFile.isEmpty() ? i_SourceLocation : i_SalvagedFile ); + impl_attachResource( sLogicalDocumentURL, aMediaDescriptor.getPropertyValues(), aGuard ); + // <- SYNCHRONIZED + } + catch( const IOException& ) + { + throw; + } + catch( const RuntimeException& ) + { + throw; + } + catch( const WrappedTargetException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetException( OUString(), *this, aError ); + } +} + +// XModel +sal_Bool SAL_CALL ODatabaseDocument::attachResource( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + if (_rURL.isEmpty() && _rArguments.getLength() == 1 && _rArguments[0].Name == "SetEmbedded") + { + m_bEmbedded = true; + return true; + } + + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + bool bRet = false; + try + { + bRet = impl_attachResource( _rURL, _rArguments, aGuard ); + } + catch( const RuntimeException& ) + { + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + throw WrappedTargetRuntimeException( OUString(), *this, aError ); + } + return bRet; +} + +bool ODatabaseDocument::impl_attachResource( const OUString& i_rLogicalDocumentURL, + const Sequence< PropertyValue >& i_rMediaDescriptor, DocumentGuard& _rDocGuard ) +{ + if (i_rLogicalDocumentURL == getURL()) + { + ::comphelper::NamedValueCollection aArgs(i_rMediaDescriptor); + + // this misuse of attachresource is a hack of the Basic importer code + // repurposing existing interfaces for uses it probably wasn't intended + // for + + // we do not support macro signatures, so we can ignore that request + aArgs.remove("BreakMacroSignature"); + + bool bMacroEventRead = false; + if ((aArgs.get( "MacroEventRead" ) >>= bMacroEventRead) && bMacroEventRead) + m_pImpl->m_bMacroCallsSeenWhileLoading = true; + aArgs.remove( "MacroEventRead" ); + + if (aArgs.empty()) + return false; + } + + // if no URL has been provided, the caller was lazy enough to not call our getURL - which is not allowed anymore, + // now since getURL and getLocation both return the same, so calling one of those should be simple. + OUString sDocumentURL( i_rLogicalDocumentURL ); + OSL_ENSURE( !sDocumentURL.isEmpty(), "ODatabaseDocument::impl_attachResource: invalid URL!" ); + if ( sDocumentURL.isEmpty() ) + sDocumentURL = getURL(); + + m_pImpl->setResource( sDocumentURL, i_rMediaDescriptor ); + + if ( impl_isInitializing() ) + { // this means we've just been loaded, and this is the attachResource call which follows + // the load call. + impl_setInitialized(); + + // determine whether the document as a whole, or sub documents, have macros. Especially the latter + // controls the availability of our XEmbeddedScripts and XScriptInvocationContext interfaces, and we + // should know this before anybody actually uses the object. + m_bAllowDocumentScripting = ( m_pImpl->determineEmbeddedMacros() != ODatabaseModelImpl::eSubDocumentMacros ); + + _rDocGuard.clear(); + // <- SYNCHRONIZED + m_aEventNotifier.notifyDocumentEvent( "OnLoadFinished" ); + } + + return true; +} + +OUString SAL_CALL ODatabaseDocument::getURL( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getURL(); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getArgs( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getMediaDescriptor().getPropertyValues(); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getArgs2( const ::css::uno::Sequence< ::rtl::OUString >& requestedArgs ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + std::vector aRet; + for (const auto & rArgName : requestedArgs) + aRet.push_back(PropertyValue(rArgName, 0, m_pImpl->getMediaDescriptor().get(rArgName), PropertyState_DIRECT_VALUE)); + return comphelper::containerToSequence(aRet); +} + +void SAL_CALL ODatabaseDocument::setArgs(const Sequence& /* aArgs */) +{ + throw NoSupportException(); +} + +void SAL_CALL ODatabaseDocument::connectController( const Reference< XController >& _xController ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + +#if OSL_DEBUG_LEVEL > 0 + for (auto const& controller : m_aControllers) + { + OSL_ENSURE( controller != _xController, "ODatabaseDocument::connectController: this controller is already connected!" ); + } +#endif + + m_aControllers.push_back( _xController ); + + m_aEventNotifier.notifyDocumentEventAsync( "OnViewCreated", Reference< XController2 >( _xController, UNO_QUERY ) ); + + bool bFirstControllerEver = m_aViewMonitor.onControllerConnected( _xController ); + if ( !bFirstControllerEver ) + return; + + // check/adjust our macro mode. + m_pImpl->checkMacrosOnLoading(); +} + +void SAL_CALL ODatabaseDocument::disconnectController( const Reference< XController >& _xController ) +{ + bool bNotifyViewClosed = false; + bool bLastControllerGone = false; + bool bIsClosing = false; + + // SYNCHRONIZED -> + { + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Controllers::iterator pos = std::find( m_aControllers.begin(), m_aControllers.end(), _xController ); + OSL_ENSURE( pos != m_aControllers.end(), "ODatabaseDocument::disconnectController: don't know this controller!" ); + if ( pos != m_aControllers.end() ) + { + m_aControllers.erase( pos ); + bNotifyViewClosed = true; + } + + if ( m_xCurrentController == _xController ) + m_xCurrentController = nullptr; + + bLastControllerGone = m_aControllers.empty(); + bIsClosing = m_bClosing; + } + // <- SYNCHRONIZED + + if ( bNotifyViewClosed ) + m_aEventNotifier.notifyDocumentEvent( "OnViewClosed", Reference< XController2 >( _xController, UNO_QUERY ) ); + + if ( !bLastControllerGone || bIsClosing ) + return; + + // if this was the last view, close the document as a whole + // #i51157# + try + { + close( true ); + } + catch( const CloseVetoException& ) + { + // okay, somebody vetoed and took ownership + } +} + +void SAL_CALL ODatabaseDocument::lockControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + ++m_pImpl->m_nControllerLockCount; +} + +void SAL_CALL ODatabaseDocument::unlockControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + --m_pImpl->m_nControllerLockCount; +} + +sal_Bool SAL_CALL ODatabaseDocument::hasControllersLocked( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_pImpl->m_nControllerLockCount != 0; +} + +Reference< XController > SAL_CALL ODatabaseDocument::getCurrentController() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_xCurrentController.is() ? m_xCurrentController : ( m_aControllers.empty() ? Reference< XController >() : *m_aControllers.begin() ); +} + +void SAL_CALL ODatabaseDocument::setCurrentController( const Reference< XController >& _xController ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + m_xCurrentController = _xController; + + if ( !m_aViewMonitor.onSetCurrentController( _xController ) ) + return; + + // check if there are sub components to recover from our document storage + bool bAttemptRecovery = m_bHasBeenRecovered; + if ( !bAttemptRecovery && m_pImpl->getMediaDescriptor().has( "ForceRecovery" ) ) + // do not use getOrDefault, it will throw for invalid types, which is not desired here + m_pImpl->getMediaDescriptor().get( "ForceRecovery" ) >>= bAttemptRecovery; + + if ( !bAttemptRecovery ) + return; + + try + { + DatabaseDocumentRecovery aDocRecovery( m_pImpl->m_aContext ); + aDocRecovery.recoverSubDocuments( m_pImpl->getRootStorage(), _xController ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< XInterface > SAL_CALL ODatabaseDocument::getCurrentSelection( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XInterface > xRet; + Reference< XSelectionSupplier > xDocView( getCurrentController(), UNO_QUERY ); + if ( xDocView.is() ) + xRet.set(xDocView->getSelection(),UNO_QUERY); + + return xRet; +} + +// XStorable +sal_Bool SAL_CALL ODatabaseDocument::hasLocation( ) +{ + return !getLocation().isEmpty(); +} + +OUString SAL_CALL ODatabaseDocument::getLocation( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getURL(); + // both XStorable::getLocation and XModel::getURL have to return the URL of the document, *not* + // the location of the file which the document was possibly recovered from (which would be getDocFileLocation) +} + +sal_Bool SAL_CALL ODatabaseDocument::isReadonly( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->m_bDocumentReadOnly; +} + +void SAL_CALL ODatabaseDocument::store( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + OUString sDocumentURL( m_pImpl->getURL() ); + if ( !sDocumentURL.isEmpty() ) + { + if ( m_pImpl->getDocFileLocation() == m_pImpl->getURL() ) + if ( m_pImpl->m_bDocumentReadOnly ) + throw IOException(); + + impl_storeAs_throw( m_pImpl->getURL(), m_pImpl->getMediaDescriptor(), SAVE, aGuard ); + return; + } + + // if we have no URL, but did survive the DocumentGuard above, then we've been inited via XLoadable::initNew, + // i.e. we're based on a temporary storage + OSL_ENSURE( m_pImpl->getDocFileLocation().isEmpty(), "ODatabaseDocument::store: unexpected URL inconsistency!" ); + + try + { + impl_storeToStorage_throw( m_pImpl->getRootStorage(), m_pImpl->getMediaDescriptor().getPropertyValues(), aGuard ); + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + if ( aError.isExtractableTo( ::cppu::UnoType< IOException >::get() ) + || aError.isExtractableTo( ::cppu::UnoType< RuntimeException >::get() ) + ) + { + // allowed to leave + throw; + } + impl_throwIOExceptionCausedBySave_throw( aError, {} ); + } +} + +void ODatabaseDocument::impl_throwIOExceptionCausedBySave_throw( const Any& i_rError, std::u16string_view i_rTargetURL ) const +{ + OUString sErrorMessage = extractExceptionMessage( m_pImpl->m_aContext, i_rError ); + sErrorMessage = ResourceManager::loadString( + RID_STR_ERROR_WHILE_SAVING, + "$location$", i_rTargetURL, + "$message$", sErrorMessage + ); + throw IOException( sErrorMessage, *const_cast< ODatabaseDocument* >( this ) ); +} + +void ODatabaseDocument::impl_storeAs_throw( const OUString& _rURL, const ::comphelper::NamedValueCollection& _rArguments, + const StoreType _eType, DocumentGuard& _rGuard ) +{ + OSL_PRECOND( ( _eType == SAVE ) || ( _eType == SAVE_AS ), + "ODatabaseDocument::impl_storeAs_throw: you introduced a new type which cannot be handled here!" ); + + // if we're in the process of initializing the document (which effectively means it is an implicit + // initialization triggered in storeAsURL), the we do not notify events, since to an observer, the SaveAs + // should not be noticeable + bool bIsInitializationProcess = impl_isInitializing(); + + if ( !bIsInitializationProcess ) + { + _rGuard.clear(); + m_aEventNotifier.notifyDocumentEvent( _eType == SAVE ? "OnSave" : "OnSaveAs", nullptr, Any( _rURL ) ); + _rGuard.reset(); + } + + Reference< XStorage > xNewRootStorage; + // will be non-NULL if our storage changed + + try + { + ModifyLock aLock( *this ); + // ignore all changes of our "modified" state during storing + + bool bLocationChanged = ( _rURL != m_pImpl->getDocFileLocation() ); + if ( bLocationChanged ) + { + // create storage for target URL + uno::Reference xTargetStorage( + impl_GetStorageOrCreateFor_throw(_rArguments, _rURL)); + + if ( m_pImpl->isEmbeddedDatabase() ) + m_pImpl->clearConnections(); + + // commit everything + m_pImpl->commitEmbeddedStorage(); + m_pImpl->commitStorages(); + + // copy own storage to target storage + Reference< XStorage > xCurrentStorage( m_pImpl->getRootStorage() ); + if ( xCurrentStorage.is() ) + xCurrentStorage->copyToStorage( xTargetStorage ); + + m_pImpl->disposeStorages(); + + // each and every document definition obtained via m_xForms and m_xReports depends + // on the sub storages which we just disposed. So, dispose the forms/reports collections, too. + // This ensures that they're re-created when needed. + clearObjectContainer( m_xForms ); + clearObjectContainer( m_xReports ); + + xNewRootStorage = m_pImpl->switchToStorage( xTargetStorage ); + + m_pImpl->m_bDocumentReadOnly = false; + } + + // store to current storage + Reference< XStorage > xCurrentStorage( m_pImpl->getOrCreateRootStorage(), UNO_SET_THROW ); + Sequence< PropertyValue > aMediaDescriptor( lcl_appendFileNameToDescriptor( _rArguments, _rURL ) ); + impl_storeToStorage_throw( xCurrentStorage, aMediaDescriptor, _rGuard ); + + // success - tell our impl + m_pImpl->setDocFileLocation( _rURL ); + m_pImpl->setResource( _rURL, aMediaDescriptor ); + + // if we are in an initialization process, then this is finished, now that we stored the document + if ( bIsInitializationProcess ) + impl_setInitialized(); + } + catch( const IOException& ) + { + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + throw; + } + catch( const RuntimeException& ) + { + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + throw; + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + + // notify the failure + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveFailed" : "OnSaveAsFailed", nullptr, Any( _rURL ) ); + + impl_throwIOExceptionCausedBySave_throw( aError, _rURL ); + } + + // notify the document event + if ( !bIsInitializationProcess ) + m_aEventNotifier.notifyDocumentEventAsync( _eType == SAVE ? "OnSaveDone" : "OnSaveAsDone", nullptr, Any( _rURL ) ); + + // reset our "modified" flag, and clear the guard + impl_setModified_nothrow( false, _rGuard ); + // <- SYNCHRONIZED + + // notify storage listeners + if ( xNewRootStorage.is() ) + impl_notifyStorageChange_nolck_nothrow( xNewRootStorage ); +} + +Reference< XStorage > ODatabaseDocument::impl_createStorageFor_throw( const OUString& _rURL ) const +{ + Reference< ucb::XSimpleFileAccess3 > xTempAccess(ucb::SimpleFileAccess::create(m_pImpl->m_aContext)); + Reference< io::XStream > xStream = xTempAccess->openFileReadWrite( _rURL ); + Reference< io::XTruncate > xTruncate(xStream,UNO_QUERY); + if ( xTruncate.is() ) + { + xTruncate->truncate(); + } + Sequence aParam{ Any(xStream), Any(ElementModes::READWRITE | ElementModes::TRUNCATE) }; + + Reference< XSingleServiceFactory > xStorageFactory( m_pImpl->createStorageFactory(), UNO_SET_THROW ); + return Reference< XStorage >( xStorageFactory->createInstanceWithArguments( aParam ), UNO_QUERY_THROW ); +} + +css::uno::Reference ODatabaseDocument::impl_GetStorageOrCreateFor_throw( + const ::comphelper::NamedValueCollection& _rArguments, const OUString& _rURL) const +{ + // Try to get the storage from arguments, then create storage for target URL + uno::Reference xTargetStorage; + _rArguments.get("TargetStorage") >>= xTargetStorage; + if (!xTargetStorage.is()) + xTargetStorage = impl_createStorageFor_throw(_rURL); + + // In case we got a StreamRelPath, then xTargetStorage should reference that sub-storage. + OUString sStreamRelPath = _rArguments.getOrDefault("StreamRelPath", OUString()); + if (!sStreamRelPath.isEmpty()) + xTargetStorage + = xTargetStorage->openStorageElement(sStreamRelPath, embed::ElementModes::READWRITE); + + return xTargetStorage; +} + +void SAL_CALL ODatabaseDocument::storeAsURL( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + + // Normally, a document initialization is done via XLoadable::load or XLoadable::initNew. For convenience + // reasons, and to not break existing API clients, it's allowed to call storeAsURL without having initialized + // the document, in which case the initialization will be done implicitly. + bool bImplicitInitialization = !impl_isInitialized(); + // implicit initialization while another initialization is just running is not possible + if ( bImplicitInitialization && impl_isInitializing() ) + throw RuntimeException(); + + if ( bImplicitInitialization ) + impl_setInitializing(); + + try + { + impl_storeAs_throw( _rURL, _rArguments, SAVE_AS, aGuard ); + // <- SYNCHRONIZED + + // impl_storeAs_throw cleared the lock on our mutex, but the below lines need this lock + // SYNCHRONIZED -> + aGuard.reset(); + + // our title might have changed, potentially at least + // Sadly, we cannot check this: Calling getTitle here and now would not deliver + // an up-to-date result, as the call is delegated to our TitleHelper instance, which itself + // updates its title only if it gets the OnSaveAsDone event (which was sent asynchronously + // by impl_storeAs_throw). So, we simply notify always, and also asynchronously + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + } + catch( const Exception& ) + { + impl_reset_nothrow(); + throw; + } + + if ( bImplicitInitialization ) + m_bAllowDocumentScripting = true; + + aGuard.clear(); + // <- SYNCHRONIZED + + if ( bImplicitInitialization ) + m_aEventNotifier.notifyDocumentEvent( "OnCreate" ); +} + +void ODatabaseDocument::impl_storeToStorage_throw( const Reference< XStorage >& _rxTargetStorage, const Sequence< PropertyValue >& _rMediaDescriptor, + DocumentGuard& _rDocGuard ) const +{ + if ( !_rxTargetStorage.is() ) + throw IllegalArgumentException( OUString(), *const_cast< ODatabaseDocument* >( this ), 1 ); + + if ( !m_pImpl.is() ) + throw DisposedException( OUString(), *const_cast< ODatabaseDocument* >( this ) ); + + try + { + // commit everything + m_pImpl->commitEmbeddedStorage(); + m_pImpl->commitStorages(); + + // copy own storage to target storage + if ( impl_isInitialized() ) + { + Reference< XStorage > xCurrentStorage = m_pImpl->getOrCreateRootStorage(); + // Root storage may be empty in case of embedding. + if ( xCurrentStorage.is() && xCurrentStorage != _rxTargetStorage ) + xCurrentStorage->copyToStorage( _rxTargetStorage ); + } + + // write into target storage + ::comphelper::NamedValueCollection aWriteArgs( _rMediaDescriptor ); + lcl_triggerStatusIndicator_throw( aWriteArgs, _rDocGuard, true ); + impl_writeStorage_throw( _rxTargetStorage, aWriteArgs ); + lcl_triggerStatusIndicator_throw( aWriteArgs, _rDocGuard, false ); + + // commit target storage + m_pImpl->commitStorageIfWriteable_ignoreErrors(_rxTargetStorage); + } + catch( const IOException& ) { throw; } + catch( const RuntimeException& ) { throw; } + catch ( const Exception& e ) + { + throw IOException( e.Message, *const_cast< ODatabaseDocument* >( this ) ); + } +} + +void SAL_CALL ODatabaseDocument::storeToURL( const OUString& _rURL, const Sequence< PropertyValue >& _rArguments ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + ModifyLock aLock( *this ); + + { + aGuard.clear(); + m_aEventNotifier.notifyDocumentEvent( "OnSaveTo", nullptr, Any( _rURL ) ); + aGuard.reset(); + } + + try + { + const ::comphelper::NamedValueCollection aArguments(_rArguments); + // create storage for target URL + Reference xTargetStorage(impl_GetStorageOrCreateFor_throw(aArguments, _rURL)); + + // extend media descriptor with URL + Sequence aMediaDescriptor(lcl_appendFileNameToDescriptor(aArguments, _rURL)); + + // store to this storage + impl_storeToStorage_throw( xTargetStorage, aMediaDescriptor, aGuard ); + } + catch( const Exception& ) + { + Any aError = ::cppu::getCaughtException(); + m_aEventNotifier.notifyDocumentEventAsync( "OnSaveToFailed", nullptr, aError ); + + if ( aError.isExtractableTo( ::cppu::UnoType< IOException >::get() ) + || aError.isExtractableTo( ::cppu::UnoType< RuntimeException >::get() ) + ) + { + // allowed to leave + throw; + } + + impl_throwIOExceptionCausedBySave_throw( aError, _rURL ); + } + + m_aEventNotifier.notifyDocumentEventAsync( "OnSaveToDone", nullptr, Any( _rURL ) ); +} + +// XModifyBroadcaster +void SAL_CALL ODatabaseDocument::addModifyListener( const Reference< XModifyListener >& _xListener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aModifyListeners.addInterface(_xListener); +} + +void SAL_CALL ODatabaseDocument::removeModifyListener( const Reference< XModifyListener >& _xListener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aModifyListeners.removeInterface(_xListener); +} + +// XModifiable +sal_Bool SAL_CALL ODatabaseDocument::isModified( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + return m_pImpl->m_bModified; +} + +void SAL_CALL ODatabaseDocument::setModified( sal_Bool _bModified ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + if ( impl_isInitialized() ) + impl_setModified_nothrow( _bModified, aGuard ); + // it's allowed to call setModified without the document being initialized already. In this case, + // we simply ignore the call - when the initialization is finished, the respective code will set + // a proper "modified" flag +} + +void ODatabaseDocument::impl_setModified_nothrow( bool _bModified, DocumentGuard& _rGuard ) +{ + // SYNCHRONIZED -> + bool bModifiedChanged = ( m_pImpl->m_bModified != _bModified ) && ( !m_pImpl->isModifyLocked() ); + + if ( bModifiedChanged ) + { + m_pImpl->m_bModified = _bModified; + m_aEventNotifier.notifyDocumentEventAsync( "OnModifyChanged" ); + } + _rGuard.clear(); + // <- SYNCHRONIZED + + if ( bModifiedChanged ) + { + lang::EventObject aEvent( *this ); + m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvent ); + } +} + +// css::document::XEventBroadcaster +void SAL_CALL ODatabaseDocument::addEventListener(const uno::Reference< document::XEventListener >& Listener ) +{ + m_aEventNotifier.addLegacyEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeEventListener( const uno::Reference< document::XEventListener >& Listener ) +{ + m_aEventNotifier.removeLegacyEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) +{ + m_aEventNotifier.addDocumentEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) +{ + m_aEventNotifier.removeDocumentEventListener( Listener ); +} + +void SAL_CALL ODatabaseDocument::notifyDocumentEvent( const OUString& EventName, const Reference< XController2 >& ViewController, const Any& Supplement ) +{ + if ( EventName.isEmpty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + if ( !DocumentEvents::needsSynchronousNotification( EventName ) ) + { + m_aEventNotifier.notifyDocumentEventAsync( EventName, ViewController, Supplement ); + return; + } + aGuard.clear(); + // <- SYNCHRONIZED + + m_aEventNotifier.notifyDocumentEvent( EventName, ViewController, Supplement ); +} + +Sequence< PropertyValue > SAL_CALL ODatabaseDocument::getPrinter( ) +{ + OSL_FAIL( "ODatabaseDocument::getPrinter: not supported!" ); + return Sequence< PropertyValue >(); +} + +void SAL_CALL ODatabaseDocument::setPrinter( const Sequence< PropertyValue >& /*aPrinter*/ ) +{ + OSL_FAIL( "ODatabaseDocument::setPrinter: not supported!" ); +} + +void SAL_CALL ODatabaseDocument::print( const Sequence< PropertyValue >& /*xOptions*/ ) +{ + OSL_FAIL( "ODatabaseDocument::print: not supported!" ); +} + +void ODatabaseDocument::impl_reparent_nothrow( const WeakReference< XNameAccess >& _rxContainer ) +{ + Reference< XChild > xChild( _rxContainer.get(), UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( *this ); +} + +void ODatabaseDocument::clearObjectContainer( WeakReference< XNameAccess >& _rxContainer) +{ + Reference< XNameAccess > xContainer = _rxContainer; + ::comphelper::disposeComponent( xContainer ); + + Reference< XChild > xChild( _rxContainer.get(),UNO_QUERY ); + if ( xChild.is() ) + xChild->setParent( nullptr ); + _rxContainer.clear(); +} + +Reference< XNameAccess > ODatabaseDocument::impl_getDocumentContainer_throw( ODatabaseModelImpl::ObjectType _eType ) +{ + if ( ( _eType != ODatabaseModelImpl::E_FORM ) && ( _eType != ODatabaseModelImpl::E_REPORT ) ) + throw IllegalArgumentException(); + + bool bFormsContainer = _eType == ODatabaseModelImpl::E_FORM; + + WeakReference< XNameAccess >& rContainerRef( bFormsContainer ? m_xForms : m_xReports ); + Reference< XNameAccess > xContainer = rContainerRef; + if ( !xContainer.is() ) + { + Any aValue; + css::uno::Reference< css::uno::XInterface > xMy(*this); + if ( dbtools::getDataSourceSetting(xMy,bFormsContainer ? "Forms" : "Reports",aValue) ) + { + OUString sSupportService; + aValue >>= sSupportService; + if ( !sSupportService.isEmpty() ) + { + Sequence aArgs{ Any(NamedValue("DatabaseDocument",Any(xMy))) }; + xContainer.set( + m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(sSupportService, aArgs, m_pImpl->m_aContext), + UNO_QUERY); + rContainerRef = xContainer; + } + } + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( _eType ) ); + rContainerRef = xContainer = new ODocumentContainer( m_pImpl->m_aContext, *this, rContainerData, bFormsContainer ); + } + impl_reparent_nothrow( xContainer ); + } + return xContainer; +} + +void ODatabaseDocument::impl_closeControllerFrames_nolck_throw( bool _bDeliverOwnership ) +{ + Controllers aCopy = m_aControllers; + + for (auto const& elem : aCopy) + { + if ( !elem.is() ) + continue; + + try + { + Reference< XCloseable> xFrame( elem->getFrame(), UNO_QUERY ); + if ( xFrame.is() ) + xFrame->close( _bDeliverOwnership ); + } + catch( const CloseVetoException& ) { throw; } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void ODatabaseDocument::impl_disposeControllerFrames_nothrow() +{ + Controllers aCopy; + aCopy.swap( m_aControllers ); // ensure m_aControllers is empty afterwards + for( const auto& rController : aCopy ) + { + try + { + if( rController.is() ) + { + Reference< XFrame > xFrame( rController->getFrame() ); + ::comphelper::disposeComponent( xFrame ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void SAL_CALL ODatabaseDocument::close(sal_Bool bDeliverOwnership) +{ + // nearly everything below can/must be done without our mutex locked, the below is just for + // the checks for being disposed and the like + // SYNCHRONIZED -> + { + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + assert (!m_bClosing); + m_bClosing = true; + } + // <- SYNCHRONIZED + + try + { + // allow listeners to veto + lang::EventObject aEvent( *this ); + m_aCloseListener.forEach( + [&aEvent, &bDeliverOwnership] (uno::Reference const& xListener) { + return xListener->queryClosing(aEvent, bDeliverOwnership); + }); + + // notify that we're going to unload + m_aEventNotifier.notifyDocumentEvent( "OnPrepareUnload" ); + + impl_closeControllerFrames_nolck_throw( bDeliverOwnership ); + + m_aCloseListener.notifyEach( &XCloseListener::notifyClosing, const_cast(aEvent) ); + + dispose(); + } + catch ( const Exception& ) + { + SolarMutexGuard g; + m_bClosing = false; + throw; + } + + // SYNCHRONIZED -> + SolarMutexGuard g; + m_bClosing = false; + // <- SYNCHRONIZED +} + +void SAL_CALL ODatabaseDocument::addCloseListener( const Reference< css::util::XCloseListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aCloseListener.addInterface(Listener); +} + +void SAL_CALL ODatabaseDocument::removeCloseListener( const Reference< css::util::XCloseListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aCloseListener.removeInterface(Listener); +} + +Reference< XNameAccess > SAL_CALL ODatabaseDocument::getFormDocuments( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getDocumentContainer_throw( ODatabaseModelImpl::E_FORM ); +} + +Reference< XNameAccess > SAL_CALL ODatabaseDocument::getReportDocuments( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getDocumentContainer_throw( ODatabaseModelImpl::E_REPORT ); +} + +void ODatabaseDocument::WriteThroughComponent( const Reference< XComponent >& xComponent, const char* pStreamName, + const char* pServiceName, const Sequence< Any >& _rArguments, const Sequence< PropertyValue >& rMediaDesc, + const Reference& _xStorageToSaveTo ) const +{ + OSL_ENSURE( pStreamName, "Need stream name!" ); + OSL_ENSURE( pServiceName, "Need service name!" ); + + // open stream + OUString sStreamName = OUString::createFromAscii( pStreamName ); + Reference< XStream > xStream = _xStorageToSaveTo->openStreamElement( sStreamName, ElementModes::READWRITE | ElementModes::TRUNCATE ); + if ( !xStream.is() ) + return; + + Reference< XOutputStream > xOutputStream( xStream->getOutputStream() ); + OSL_ENSURE( xOutputStream.is(), "Can't create output stream in package!" ); + if ( !xOutputStream.is() ) + return; + + Reference< XSeekable > xSeek( xOutputStream, UNO_QUERY ); + if ( xSeek.is() ) + xSeek->seek(0); + + Reference< XPropertySet > xStreamProp( xOutputStream, UNO_QUERY_THROW ); + xStreamProp->setPropertyValue( INFO_MEDIATYPE, Any( OUString( "text/xml" ) ) ); + xStreamProp->setPropertyValue( "Compressed", Any( true ) ); + + // write the stuff + WriteThroughComponent( xOutputStream, xComponent, pServiceName, _rArguments, rMediaDesc ); +} + +void ODatabaseDocument::WriteThroughComponent( const Reference< XOutputStream >& xOutputStream, + const Reference< XComponent >& xComponent, const char* pServiceName, const Sequence< Any >& _rArguments, + const Sequence< PropertyValue >& rMediaDesc ) const +{ + OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" ); + OSL_ENSURE( xComponent.is(), "Need component!" ); + OSL_ENSURE( nullptr != pServiceName, "Need component name!" ); + + // get component + Reference< XWriter > xSaxWriter = xml::sax::Writer::create( m_pImpl->m_aContext ); + + // connect XML writer to output stream + xSaxWriter->setOutputStream( xOutputStream ); + + // prepare arguments (prepend doc handler to given arguments) + Sequence aArgs( 1 + _rArguments.getLength() ); + auto pArgs = aArgs.getArray(); + pArgs[0] <<= xSaxWriter; + for ( sal_Int32 i = 0; i < _rArguments.getLength(); ++i ) + pArgs[ i+1 ] = _rArguments[i]; + + // get filter component + Reference< XExporter > xExporter( m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(OUString::createFromAscii(pServiceName), aArgs, m_pImpl->m_aContext), UNO_QUERY_THROW ); + + // connect model and filter + xExporter->setSourceDocument( xComponent ); + + // filter + Reference< XFilter > xFilter( xExporter, UNO_QUERY_THROW ); + xFilter->filter( rMediaDesc ); +} + +void ODatabaseDocument::impl_writeStorage_throw( const Reference< XStorage >& _rxTargetStorage, const ::comphelper::NamedValueCollection& _rMediaDescriptor ) const +{ + // extract status indicator + Sequence< Any > aDelegatorArguments; + lcl_extractStatusIndicator( _rMediaDescriptor, aDelegatorArguments ); + + uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) ); + + xInfoSet->setPropertyValue("UsePrettyPrinting", uno::Any(officecfg::Office::Common::Save::Document::PrettyPrinting::get())); + if ( officecfg::Office::Common::Save::URL::FileSystem::get() ) + { + OUString sBaseURI = _rMediaDescriptor.getOrDefault("BaseURI", OUString()); + if (sBaseURI.isEmpty()) + sBaseURI = _rMediaDescriptor.getOrDefault("URL",OUString()); + xInfoSet->setPropertyValue("BaseURI", uno::Any(sBaseURI)); + } + + // Set TargetStorage, so it doesn't have to be re-constructed based on possibly empty URL. + xInfoSet->setPropertyValue("TargetStorage", uno::Any(m_pImpl->getRootStorage())); + + // Set StreamRelPath, in case this document is an embedded one. + OUString sStreamRelPath; + OUString sURL = _rMediaDescriptor.getOrDefault("URL", OUString()); + if (sURL.startsWithIgnoreAsciiCase("vnd.sun.star.pkg:")) + { + // In this case the host contains the real path, and the path is the embedded stream name. + INetURLObject aURL(sURL); + sStreamRelPath = aURL.GetURLPath(INetURLObject::DecodeMechanism::WithCharset); + if (sStreamRelPath.startsWith("/")) + sStreamRelPath = sStreamRelPath.copy(1); + } + if (!sStreamRelPath.isEmpty()) + xInfoSet->setPropertyValue("StreamRelPath", uno::Any(sStreamRelPath)); + + sal_Int32 nArgsLen = aDelegatorArguments.getLength(); + aDelegatorArguments.realloc(nArgsLen+1); + aDelegatorArguments.getArray()[nArgsLen++] <<= xInfoSet; + + Reference< XPropertySet > xProp( _rxTargetStorage, UNO_QUERY_THROW ); + xProp->setPropertyValue( INFO_MEDIATYPE, Any( OUString(MIMETYPE_OASIS_OPENDOCUMENT_DATABASE_ASCII) ) ); + + OUString aVersion; + SvtSaveOptions::ODFSaneDefaultVersion const nDefVersion = + GetODFSaneDefaultVersion(); + // older versions can not have this property set, + // it exists only starting from ODF1.2 + if (nDefVersion >= SvtSaveOptions::ODFSVER_013) + { + aVersion = ODFVER_013_TEXT; + } + else if (nDefVersion >= SvtSaveOptions::ODFSVER_012) + { + aVersion = ODFVER_012_TEXT; + } + + if (!aVersion.isEmpty()) + { + try + { + xProp->setPropertyValue("Version" , uno::Any(aVersion)); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("dbaccess", "exception setting Version"); + } + } + + Reference< XComponent > xComponent( *const_cast< ODatabaseDocument* >( this ), UNO_QUERY_THROW ); + + Sequence< PropertyValue > aMediaDescriptor; + _rMediaDescriptor >>= aMediaDescriptor; + + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("settings.xml"))); + WriteThroughComponent( xComponent, "settings.xml", "com.sun.star.comp.sdb.XMLSettingsExporter", + aDelegatorArguments, aMediaDescriptor, _rxTargetStorage ); + + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + WriteThroughComponent( xComponent, "content.xml", "com.sun.star.comp.sdb.DBExportFilter", + aDelegatorArguments, aMediaDescriptor, _rxTargetStorage ); + + if ( _rxTargetStorage->hasByName ( sPictures ) ) + { + try + { + // Delete any previously existing Pictures folder and regenerate + // any needed content if needed + Reference< XStorageBasedLibraryContainer > xDlgs = m_pImpl->getLibraryContainer( false ); + if ( xDlgs.is() ) + { + Reference< XModel > xModel(const_cast< ODatabaseDocument*>(this)); + lcl_uglyHackToStoreDialogeEmbedImages( m_pImpl->getLibraryContainer(false), _rxTargetStorage, xModel, m_pImpl->m_aContext ); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + m_pImpl->storeLibraryContainersTo( _rxTargetStorage ); +} + +Reference< XUIConfigurationManager > SAL_CALL ODatabaseDocument::getUIConfigurationManager( ) +{ + return Reference< XUIConfigurationManager >( getUIConfigurationManager2(), UNO_QUERY_THROW ); +} + +Reference< XUIConfigurationManager2 > const & ODatabaseDocument::getUIConfigurationManager2( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + if ( !m_xUIConfigurationManager.is() ) + { + m_xUIConfigurationManager = UIConfigurationManager::create( m_pImpl->m_aContext ); + + OUString aUIConfigFolderName( "Configurations2" ); + + // First try to open with READWRITE and then READ + Reference< XStorage > xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, ElementModes::READWRITE ); + if ( xConfigStorage.is() ) + { + OUString aMediaType; + Reference< XPropertySet > xPropSet( xConfigStorage, UNO_QUERY ); + Any a = xPropSet->getPropertyValue( INFO_MEDIATYPE ); + if ( !( a >>= aMediaType ) || aMediaType.isEmpty() ) + { + a <<= OUString("application/vnd.sun.xml.ui.configuration"); + xPropSet->setPropertyValue( INFO_MEDIATYPE, a ); + } + } + else + xConfigStorage = getDocumentSubStorage( aUIConfigFolderName, ElementModes::READ ); + + // initialize ui configuration manager with document substorage + m_xUIConfigurationManager->setStorage( xConfigStorage ); + } + + return m_xUIConfigurationManager; +} + +Reference< XStorage > SAL_CALL ODatabaseDocument::getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XDocumentSubStorageSupplier > xStorageAccess( m_pImpl->getDocumentSubStorageSupplier() ); + return xStorageAccess->getDocumentSubStorage( aStorageName, nMode ); +} + +Sequence< OUString > SAL_CALL ODatabaseDocument::getDocumentSubStoragesNames( ) +{ + Reference< XDocumentSubStorageSupplier > xStorageAccess( m_pImpl->getDocumentSubStorageSupplier() ); + return xStorageAccess->getDocumentSubStoragesNames(); +} + +void ODatabaseDocument::impl_notifyStorageChange_nolck_nothrow( const Reference< XStorage >& xNewRootStorage ) +{ + Reference< XInterface > xMe( *this ); + + m_aStorageListeners.forEach( + [&xMe, &xNewRootStorage] (uno::Reference const& xListener) { + return xListener->notifyStorageChange(xMe, xNewRootStorage); + }); +} + +void ODatabaseDocument::disposing() +{ + if ( !m_pImpl.is() ) + { + // this means that we're already disposed + OSL_ENSURE( ODatabaseDocument_OfficeDocument::rBHelper.bDisposed, "ODatabaseDocument::disposing: no impl anymore, but not yet disposed!" ); + return; + } + + if ( impl_isInitialized() ) + m_aEventNotifier.notifyDocumentEvent( "OnUnload" ); + + Reference< XModel > xHoldAlive( this ); + + m_aEventNotifier.disposing(); + + lang::EventObject aDisposeEvent(static_cast(this)); + m_aModifyListeners.disposeAndClear( aDisposeEvent ); + m_aCloseListener.disposeAndClear( aDisposeEvent ); + m_aStorageListeners.disposeAndClear( aDisposeEvent ); + + // this is the list of objects which we currently hold as member. Upon resetting + // those members, we can (potentially) release the last reference to them, in which + // case they will be deleted - if they're C++ implementations, that is :). + // Some of those implementations are offending enough to require the SolarMutex, which + // means we should not release the last reference while our own mutex is locked ... + std::vector< Reference< XInterface > > aKeepAlive; + + // SYNCHRONIZED -> + { + SolarMutexGuard aGuard; + + OSL_ENSURE(m_aControllers.empty(), + "ODatabaseDocument::disposing: there still are controllers!"); + // normally, nobody should explicitly dispose, but only XCloseable::close + // the document. And upon closing, our controllers are closed, too + + { + uno::Reference xUIInterface(m_xUIConfigurationManager); + aKeepAlive.push_back(xUIInterface); + } + m_xUIConfigurationManager = nullptr; + + clearObjectContainer(m_xForms); + clearObjectContainer(m_xReports); + + // reset the macro mode: in case the our impl struct stays alive (e.g. because our DataSource + // object still exists), and somebody subsequently re-opens the document, we want to have + // the security warning, again. + m_pImpl->resetMacroExecutionMode(); + + // similar arguing for our ViewMonitor + m_aViewMonitor.reset(); + + // tell our Impl to forget us + m_pImpl->modelIsDisposing(impl_isInitialized(), ODatabaseModelImpl::ResetModelAccess()); + + // now, at the latest, the controller array should be empty. Controllers are + // expected to listen for our disposal, and disconnect then + OSL_ENSURE(m_aControllers.empty(), + "ODatabaseDocument::disposing: there still are controllers!"); + impl_disposeControllerFrames_nothrow(); + + { + uno::Reference xModuleInterface(m_xModuleManager); + aKeepAlive.push_back(xModuleInterface); + } + m_xModuleManager.clear(); + + { + uno::Reference xTitleInterface(m_xTitleHelper); + aKeepAlive.push_back(xTitleInterface); + } + m_xTitleHelper.clear(); + + m_pImpl.clear(); + } + // <- SYNCHRONIZED + + aKeepAlive.clear(); +} + +// XComponent +void SAL_CALL ODatabaseDocument::dispose( ) +{ + ::cppu::WeakComponentImplHelperBase::dispose(); +} + +void SAL_CALL ODatabaseDocument::addEventListener( const Reference< lang::XEventListener >& _xListener ) +{ + ::cppu::WeakComponentImplHelperBase::addEventListener( _xListener ); +} + +void SAL_CALL ODatabaseDocument::removeEventListener( const Reference< lang::XEventListener >& _xListener ) +{ + ::cppu::WeakComponentImplHelperBase::removeEventListener( _xListener ); +} + +// XServiceInfo +OUString ODatabaseDocument::getImplementationName() +{ + return "com.sun.star.comp.dba.ODatabaseDocument"; +} + +Sequence< OUString > ODatabaseDocument::getSupportedServiceNames() +{ + return { "com.sun.star.sdb.OfficeDatabaseDocument", "com.sun.star.document.OfficeDocument" }; +} + +sal_Bool ODatabaseDocument::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +Reference< XDataSource > SAL_CALL ODatabaseDocument::getDataSource() +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodWithoutInit ); + return m_pImpl->getOrCreateDataSource(); +} + +namespace +{ +/// Property map for embedded import info set. +comphelper::PropertyMapEntry const aEmbeddedImportInfoMap[] = +{ + {OUString("StreamRelPath"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + {OUString("StreamName"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, + {OUString("SourceStorage"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, +}; +} + +void SAL_CALL ODatabaseDocument::loadFromStorage(const Reference& xStorage, const Sequence& rMediaDescriptor) +{ + DocumentGuard aGuard(*this, DocumentGuard::InitMethod); + + uno::Reference xInfoSet(comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aEmbeddedImportInfoMap))); + xInfoSet->setPropertyValue("StreamRelPath", uno::Any(comphelper::NamedValueCollection::getOrDefault(rMediaDescriptor, u"HierarchicalDocumentName", OUString()))); + xInfoSet->setPropertyValue("StreamName", uno::Any(OUString("content.xml"))); + xInfoSet->setPropertyValue("SourceStorage", uno::Any(xStorage)); + + uno::Sequence aFilterCreationArgs{ Any(xInfoSet) }; + + uno::Reference xImporter(m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.comp.sdb.DBFilter", aFilterCreationArgs, m_pImpl->m_aContext), uno::UNO_QUERY_THROW); + + uno::Reference xComponent(*this, uno::UNO_QUERY_THROW); + xImporter->setTargetDocument(xComponent); + + uno::Reference xFilter(xImporter, uno::UNO_QUERY_THROW); + uno::Sequence aFilterArgs; + xFilter->filter(aFilterArgs); + + // In case of embedding, XModel::attachResource is already called. + if (m_bEmbedded) + impl_setInitialized(); + + impl_setModified_nothrow(false, aGuard); +} + +void SAL_CALL ODatabaseDocument::storeToStorage( const Reference< XStorage >& _rxStorage, const Sequence< PropertyValue >& _rMediaDescriptor ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_storeToStorage_throw( _rxStorage, _rMediaDescriptor, aGuard ); +} + +void SAL_CALL ODatabaseDocument::switchToStorage( const Reference< XStorage >& _rxNewRootStorage ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XStorage > xNewRootStorage( m_pImpl->switchToStorage( _rxNewRootStorage ) ); + + aGuard.clear(); + impl_notifyStorageChange_nolck_nothrow( xNewRootStorage ); +} + +Reference< XStorage > SAL_CALL ODatabaseDocument::getDocumentStorage( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::MethodUsedDuringInit); + return m_pImpl->getOrCreateRootStorage(); +} + +void SAL_CALL ODatabaseDocument::addStorageChangeListener( const Reference< XStorageChangeListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aStorageListeners.addInterface( Listener ); +} + +void SAL_CALL ODatabaseDocument::removeStorageChangeListener( const Reference< XStorageChangeListener >& Listener ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + m_aStorageListeners.addInterface( Listener ); +} + +Reference< XStorageBasedLibraryContainer > SAL_CALL ODatabaseDocument::getBasicLibraries() +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return m_pImpl->getLibraryContainer( true ); +} + +Reference< XStorageBasedLibraryContainer > SAL_CALL ODatabaseDocument::getDialogLibraries() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return m_pImpl->getLibraryContainer( false ); +} + +sal_Bool SAL_CALL ODatabaseDocument::getAllowMacroExecution() +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return m_pImpl->adjustMacroMode_AutoReject(); +} + +Reference< XEmbeddedScripts > SAL_CALL ODatabaseDocument::getScriptContainer() +{ + return this; +} + +Reference< provider::XScriptProvider > SAL_CALL ODatabaseDocument::getScriptProvider( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + Reference< XScriptProvider > xScriptProvider( m_xScriptProvider ); + if ( !xScriptProvider.is() ) + { + Reference < XScriptProviderFactory > xFactory = + theMasterScriptProviderFactory::get( m_pImpl->m_aContext ); + + Any aScriptProviderContext; + if ( m_bAllowDocumentScripting ) + aScriptProviderContext <<= Reference< XModel >( this ); + + xScriptProvider.set( xFactory->createScriptProvider( aScriptProviderContext ), UNO_SET_THROW ); + m_xScriptProvider = xScriptProvider; + } + + return xScriptProvider; +} + +Reference< XNameReplace > SAL_CALL ODatabaseDocument::getEvents( ) +{ + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return m_pEventContainer.get(); +} + +Reference< XInterface > ODatabaseDocument::getThis() const +{ + return *const_cast< ODatabaseDocument* >( this ); +} + +namespace { + +struct CreateAny +{ + Any operator() (const Reference& lhs) const + { + return Any(lhs); + } +}; + +} + +// XModel2 +Reference< XEnumeration > SAL_CALL ODatabaseDocument::getControllers( ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + uno::Sequence< Any> aController( m_aControllers.size() ); + std::transform( m_aControllers.begin(), m_aControllers.end(), aController.getArray(), CreateAny() ); + return new ::comphelper::OAnyEnumeration(aController); +} + +Sequence< OUString > SAL_CALL ODatabaseDocument::getAvailableViewControllerNames( ) +{ + Sequence< OUString > aNames { SERVICE_SDB_APPLICATIONCONTROLLER }; + return aNames; +} + +Reference< XController2 > SAL_CALL ODatabaseDocument::createDefaultViewController( const Reference< XFrame >& Frame ) +{ + return createViewController( "Default", Sequence< PropertyValue >(), Frame); +} + +Reference< XController2 > SAL_CALL ODatabaseDocument::createViewController( const OUString& ViewName, const Sequence< PropertyValue >& Arguments, const Reference< XFrame >& Frame ) +{ + if ( ViewName != "Default" && ViewName != "Preview" ) + throw IllegalArgumentException( OUString(), *this, 1 ); + if ( !Frame.is() ) + throw IllegalArgumentException( OUString(), *this, 3 ); + + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + aGuard.clear(); + + Reference< XController2 > xController( + m_pImpl->m_aContext->getServiceManager()->createInstanceWithContext("org.openoffice.comp.dbu.OApplicationController", m_pImpl->m_aContext), + UNO_QUERY_THROW ); + + ::comphelper::NamedValueCollection aInitArgs( Arguments ); + aInitArgs.put( "Frame", Frame ); + if ( ViewName == "Preview" ) + aInitArgs.put( "Preview", true ); + Reference< XInitialization > xInitController( xController, UNO_QUERY_THROW ); + xInitController->initialize( aInitArgs.getWrappedPropertyValues() ); + + return xController; +} + +Reference< XTitle > const & ODatabaseDocument::impl_getTitleHelper_throw() +{ + if ( ! m_xTitleHelper.is ()) + { + Reference< XUntitledNumbers > xDesktop(Desktop::create(m_pImpl->m_aContext), uno::UNO_QUERY_THROW); + Reference< frame::XModel > xThis (getThis(), uno::UNO_QUERY_THROW); + + m_xTitleHelper = new ::framework::TitleHelper(m_pImpl->m_aContext, xThis, xDesktop); + } + + return m_xTitleHelper; +} + +uno::Reference< frame::XUntitledNumbers > ODatabaseDocument::impl_getUntitledHelper_throw(const uno::Reference< uno::XInterface >& _xComponent) +{ + if ( !m_xModuleManager.is() ) + m_xModuleManager.set( ModuleManager::create(m_pImpl->m_aContext) ); + + OUString sModuleId; + if (_xComponent.is()) + sModuleId = m_xModuleManager->identify(_xComponent); + + uno::Reference< frame::XUntitledNumbers > xNumberedControllers; + + TNumberedController::const_iterator aFind = m_aNumberedControllers.find(sModuleId); + if ( aFind == m_aNumberedControllers.end() ) + { + rtl::Reference<::comphelper::NumberedCollection> pHelper = new ::comphelper::NumberedCollection(); + xNumberedControllers = pHelper; + pHelper->setOwner(uno::Reference< frame::XModel >(this)); + + m_aNumberedControllers.emplace( sModuleId,xNumberedControllers ); + } + else + xNumberedControllers = aFind->second; + + return xNumberedControllers; +} + +// css.frame.XTitle +OUString SAL_CALL ODatabaseDocument::getTitle() +{ + // SYNCHRONIZED -> + DocumentGuard aGuard( *this, DocumentGuard::MethodUsedDuringInit ); + return impl_getTitleHelper_throw()->getTitle(); +} + +// css.frame.XTitle +void SAL_CALL ODatabaseDocument::setTitle( const OUString& sTitle ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getTitleHelper_throw()->setTitle( sTitle ); + m_aEventNotifier.notifyDocumentEventAsync( "OnTitleChanged" ); + // <- SYNCHRONIZED +} + +// css.frame.XTitleChangeBroadcaster +void SAL_CALL ODatabaseDocument::addTitleChangeListener( const uno::Reference< frame::XTitleChangeListener >& xListener ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + uno::Reference< frame::XTitleChangeBroadcaster > xBroadcaster( impl_getTitleHelper_throw(), uno::UNO_QUERY_THROW ); + xBroadcaster->addTitleChangeListener( xListener ); +} + +// css.frame.XTitleChangeBroadcaster +void SAL_CALL ODatabaseDocument::removeTitleChangeListener( const uno::Reference< frame::XTitleChangeListener >& xListener ) +{ + // SYNCHRONIZED -> + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + + uno::Reference< frame::XTitleChangeBroadcaster > xBroadcaster( impl_getTitleHelper_throw(), uno::UNO_QUERY_THROW ); + xBroadcaster->removeTitleChangeListener( xListener ); +} + +// css.frame.XUntitledNumbers +::sal_Int32 SAL_CALL ODatabaseDocument::leaseNumber( const uno::Reference< uno::XInterface >& xComponent ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + return impl_getUntitledHelper_throw(xComponent)->leaseNumber (xComponent); +} + +// css.frame.XUntitledNumbers +void SAL_CALL ODatabaseDocument::releaseNumber( ::sal_Int32 nNumber ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getUntitledHelper_throw()->releaseNumber (nNumber); +} + +// css.frame.XUntitledNumbers +void SAL_CALL ODatabaseDocument::releaseNumberForComponent( const uno::Reference< uno::XInterface >& xComponent ) +{ + DocumentGuard aGuard(*this, DocumentGuard::DefaultMethod); + impl_getUntitledHelper_throw(xComponent)->releaseNumberForComponent (xComponent); +} + +// css.frame.XUntitledNumbers +OUString SAL_CALL ODatabaseDocument::getUntitledPrefix() +{ + return OUString(); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseDocument(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + Reference xDBContextTunnel(DatabaseContext::create(context), UNO_QUERY_THROW); + dbaccess::ODatabaseContext* pContext + = comphelper::getFromUnoTunnel(xDBContextTunnel); + assert(pContext); + + rtl::Reference pImpl( + new dbaccess::ODatabaseModelImpl(context, *pContext)); + css::uno::Reference inst(pImpl->createNewModel_deliverOwnership()); + inst->acquire(); + return inst.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databasedocument.hxx b/dbaccess/source/core/dataaccess/databasedocument.hxx new file mode 100644 index 000000000..bd458d1b9 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databasedocument.hxx @@ -0,0 +1,753 @@ +/* -*- 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 . + */ +#pragma once + +#include + +#include +#include + +#include +#include "documenteventnotifier.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace comphelper { + class NamedValueCollection; +} + +namespace dbaccess +{ + +class DocumentEvents; +class DocumentEventExecutor; +class DocumentGuard; + +typedef std::vector< css::uno::Reference< css::frame::XController > > Controllers; + +// ViewMonitor +/** helper class monitoring the views of a document, and firing appropriate events + when views are attached / detached +*/ +class ViewMonitor +{ +public: + explicit ViewMonitor( DocumentEventNotifier& _rEventNotifier ) + :m_rEventNotifier( _rEventNotifier ) + ,m_bIsNewDocument( true ) + ,m_bEverHadController( false ) + ,m_bLastIsFirstEverController( false ) + ,m_xLastConnectedController() + { + } + + ViewMonitor(const ViewMonitor&) = delete; + const ViewMonitor& operator=(const ViewMonitor&) = delete; + + void reset() + { + m_bEverHadController = false; + m_bLastIsFirstEverController = false; + m_xLastConnectedController.clear(); + } + + /** to be called when a view (aka controller) has been connected to the document + @return + if and only if this was the first-ever controller connected to the document + */ + bool onControllerConnected( + const css::uno::Reference< css::frame::XController >& _rxController + ); + + /** to be called when a controller is set as current controller + @return + if and only if the controller connection indicates that loading the document is finished. This + is the case if the given controller has previously been connected, and it was the first controller + ever for which this happened. + */ + bool onSetCurrentController( + const css::uno::Reference< css::frame::XController >& _rxController + ); + + void onLoadedDocument() { m_bIsNewDocument = false; } + +private: + DocumentEventNotifier& m_rEventNotifier; + bool m_bIsNewDocument; + bool m_bEverHadController; + bool m_bLastIsFirstEverController; + css::uno::Reference< css::frame::XController > + m_xLastConnectedController; +}; + +// ODatabaseDocument +typedef cppu::PartialWeakComponentImplHelper< css::frame::XModel3 + , css::util::XModifiable + , css::frame::XStorable + , css::document::XEventBroadcaster + , css::document::XDocumentEventBroadcaster + , css::view::XPrintable + , css::util::XCloseable + , css::lang::XServiceInfo + , css::sdb::XOfficeDatabaseDocument + , css::ui::XUIConfigurationManagerSupplier + , css::document::XStorageBasedDocument + , css::document::XEmbeddedScripts + , css::document::XScriptInvocationContext + , css::script::provider::XScriptProviderSupplier + , css::document::XEventsSupplier + , css::frame::XLoadable + , css::document::XDocumentRecovery + > ODatabaseDocument_OfficeDocument; + +typedef ::cppu::ImplHelper3< css::frame::XTitle + , css::frame::XTitleChangeBroadcaster + , css::frame::XUntitledNumbers + > ODatabaseDocument_Title; + +class ODatabaseDocument :public ModelDependentComponent // ModelDependentComponent must be first! + ,public ODatabaseDocument_OfficeDocument + ,public ODatabaseDocument_Title +{ + enum InitState + { + NotInitialized, + Initializing, + Initialized + }; + + typedef std::map< OUString, css::uno::Reference< css::frame::XUntitledNumbers > > TNumberedController; + css::uno::Reference< css::ui::XUIConfigurationManager2> m_xUIConfigurationManager; + + ::comphelper::OInterfaceContainerHelper3 m_aModifyListeners; + ::comphelper::OInterfaceContainerHelper3 m_aCloseListener; + ::comphelper::OInterfaceContainerHelper3 m_aStorageListeners; + + std::unique_ptr m_pEventContainer; + ::rtl::Reference< DocumentEventExecutor > m_pEventExecutor; + DocumentEventNotifier m_aEventNotifier; + + css::uno::Reference< css::frame::XController > m_xCurrentController; + Controllers m_aControllers; + ViewMonitor m_aViewMonitor; + + css::uno::WeakReference< css::container::XNameAccess > m_xForms; + css::uno::WeakReference< css::container::XNameAccess > m_xReports; + css::uno::WeakReference< css::script::provider::XScriptProvider > m_xScriptProvider; + + /** @short such module manager is used to classify new opened documents. */ + css::uno::Reference< css::frame::XModuleManager2 > m_xModuleManager; + css::uno::Reference< css::frame::XTitle > m_xTitleHelper; + TNumberedController m_aNumberedControllers; + + /** true if and only if the DatabaseDocument's "initNew" or "load" have been called (or, well, + the document has be initialized implicitly - see storeAsURL + */ + InitState m_eInitState; + bool m_bClosing; + bool m_bAllowDocumentScripting; + bool m_bHasBeenRecovered; + /// If XModel::attachResource() was called to inform us that the document is embedded into another one. + bool m_bEmbedded; + + enum StoreType { SAVE, SAVE_AS }; + /** stores the document to the given URL, rebases it to the respective new storage, if necessary, resets + the modified flag, and notifies any listeners as required + + @param _rURL + the URL to store the document to + @param _rArguments + arguments for storing the document (MediaDescriptor) + @param _eType + the type of the store process (Save or SaveAs). The method will automatically + notify the proper events for this type. + @param _rGuard + the instance lock to be released before doing synchronous notifications + @throws css::io::IOException + @throws css::uno::RuntimeException + */ + void impl_storeAs_throw( + const OUString& _rURL, + const ::comphelper::NamedValueCollection& _rArguments, + const StoreType _eType, + DocumentGuard& _rGuard + ); + + /** notifies our storage change listeners that our underlying storage changed + + @param _rxNewRootStorage + the new root storage to be notified. If , it is assumed that no storage change actually + happened, and the listeners are not notified. + */ + void impl_notifyStorageChange_nolck_nothrow( + const css::uno::Reference< css::embed::XStorage >& _rxNewRootStorage + ); + + /// write a single XML stream into the package + void WriteThroughComponent( + const css::uno::Reference< css::lang::XComponent > & xComponent, /// the component we export + const char* pStreamName, /// the stream name + const char* pServiceName, /// service name of the component + const css::uno::Sequence< css::uno::Any> & rArguments, /// the argument (XInitialization) + const css::uno::Sequence< css::beans::PropertyValue> & rMediaDesc,/// output descriptor + const css::uno::Reference< css::embed::XStorage >& _xStorageToSaveTo + ) const; + + /// write a single output stream + /// (to be called either directly or by WriteThroughComponent(...)) + void WriteThroughComponent( + const css::uno::Reference< css::io::XOutputStream >& xOutputStream, + const css::uno::Reference< css::lang::XComponent >& xComponent, + const char* pServiceName, + const css::uno::Sequence< css::uno::Any >& rArguments, + const css::uno::Sequence< css::beans::PropertyValue> & rMediaDesc + ) const; + + /** writes the content and settings + @param sURL + The URL + @param lArguments + The media descriptor + @param _xStorageToSaveTo + The storage which should be used for saving + */ + void impl_writeStorage_throw( + const css::uno::Reference< css::embed::XStorage >& _rxTargetStorage, + const ::comphelper::NamedValueCollection& _rMediaDescriptor + ) const; + + // ModelDependentComponent overridables + virtual css::uno::Reference< css::uno::XInterface > getThis() const override; + + css::uno::Reference< css::frame::XTitle > const & impl_getTitleHelper_throw(); + css::uno::Reference< css::frame::XUntitledNumbers > impl_getUntitledHelper_throw( + const css::uno::Reference< css::uno::XInterface >& _xComponent = css::uno::Reference< css::uno::XInterface >()); + +private: + explicit ODatabaseDocument(const ::rtl::Reference& _pImpl); + // Do NOT create those documents directly, always use ODatabaseModelImpl::getModel. Reason is that + // ODatabaseDocument requires clear ownership, and in turn lifetime synchronisation with the ModelImpl. + // If you create a ODatabaseDocument directly, you might easily create a leak. + // #i50905# + +protected: + virtual void SAL_CALL disposing() override; + + virtual ~ODatabaseDocument() override; + +public: + struct FactoryAccess { friend class ODatabaseModelImpl; private: FactoryAccess() { } }; + static rtl::Reference createDatabaseDocument( const ::rtl::Reference& _pImpl, FactoryAccess /*accessControl*/ ) + { + return new ODatabaseDocument( _pImpl ); + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type& _rType) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XModel + virtual sal_Bool SAL_CALL attachResource( const OUString& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override ; + virtual OUString SAL_CALL getURL( ) override ; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getArgs( ) override ; + virtual void SAL_CALL connectController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual void SAL_CALL disconnectController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual void SAL_CALL lockControllers( ) override ; + virtual void SAL_CALL unlockControllers( ) override ; + virtual sal_Bool SAL_CALL hasControllersLocked( ) override ; + virtual css::uno::Reference< css::frame::XController > SAL_CALL getCurrentController( ) override ; + virtual void SAL_CALL setCurrentController( const css::uno::Reference< css::frame::XController >& Controller ) override ; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getCurrentSelection( ) override ; + + // XModel2 + virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL getControllers( ) override ; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableViewControllerNames( ) override ; + virtual css::uno::Reference< css::frame::XController2 > SAL_CALL createDefaultViewController( const css::uno::Reference< css::frame::XFrame >& Frame ) override ; + virtual css::uno::Reference< css::frame::XController2 > SAL_CALL createViewController( const OUString& ViewName, const css::uno::Sequence< css::beans::PropertyValue >& Arguments, const css::uno::Reference< css::frame::XFrame >& Frame ) override ; + virtual void SAL_CALL setArgs(const css::uno::Sequence& aArgs) override; + + // XModel3 + virtual ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL getArgs2( const ::css::uno::Sequence< ::rtl::OUString >& requestedArgs ) override; + + // XStorable + virtual sal_Bool SAL_CALL hasLocation( ) override ; + virtual OUString SAL_CALL getLocation( ) override ; + virtual sal_Bool SAL_CALL isReadonly( ) override ; + virtual void SAL_CALL store( ) override ; + virtual void SAL_CALL storeAsURL( const OUString& sURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override ; + virtual void SAL_CALL storeToURL( const OUString& sURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override ; + + // XModifyBroadcaster + virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override; + + // css::util::XModifiable + virtual sal_Bool SAL_CALL isModified( ) override ; + virtual void SAL_CALL setModified( sal_Bool bModified ) override ; + + // XEventBroadcaster + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::document::XEventListener >& aListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::document::XEventListener >& aListener ) override; + + // XDocumentEventBroadcaster + virtual void SAL_CALL addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override; + virtual void SAL_CALL removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ) override; + virtual void SAL_CALL notifyDocumentEvent( const OUString& EventName, const css::uno::Reference< css::frame::XController2 >& ViewController, const css::uno::Any& Supplement ) override; + + // XPrintable + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getPrinter( ) override ; + virtual void SAL_CALL setPrinter( const css::uno::Sequence< css::beans::PropertyValue >& aPrinter ) override ; + virtual void SAL_CALL print( const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override ; + + // XFormDocumentsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getFormDocuments( ) override; + + // XReportDocumentsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getReportDocuments( ) override; + + // XCloseable + virtual void SAL_CALL close( sal_Bool DeliverOwnership ) override; + virtual void SAL_CALL addCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + virtual void SAL_CALL removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& Listener ) override; + + // XUIConfigurationManagerSupplier + virtual css::uno::Reference< css::ui::XUIConfigurationManager > SAL_CALL getUIConfigurationManager( ) override; + + // XDocumentSubStorageSupplier + virtual css::uno::Reference< css::embed::XStorage > SAL_CALL getDocumentSubStorage( const OUString& aStorageName, sal_Int32 nMode ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getDocumentSubStoragesNames( ) override; + + // XOfficeDatabaseDocument + virtual css::uno::Reference< css::sdbc::XDataSource > SAL_CALL getDataSource() override; + + // XStorageBasedDocument + virtual void SAL_CALL loadFromStorage( const css::uno::Reference< css::embed::XStorage >& xStorage, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescriptor ) override; + virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& xStorage, const css::uno::Sequence< css::beans::PropertyValue >& aMediaDescriptor ) override; + virtual void SAL_CALL switchToStorage( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + virtual css::uno::Reference< css::embed::XStorage > SAL_CALL getDocumentStorage( ) override; + virtual void SAL_CALL addStorageChangeListener( const css::uno::Reference< css::document::XStorageChangeListener >& xListener ) override; + virtual void SAL_CALL removeStorageChangeListener( const css::uno::Reference< css::document::XStorageChangeListener >& xListener ) override; + + // XEmbeddedScripts + virtual css::uno::Reference< css::script::XStorageBasedLibraryContainer > SAL_CALL getBasicLibraries() override; + virtual css::uno::Reference< css::script::XStorageBasedLibraryContainer > SAL_CALL getDialogLibraries() override; + virtual sal_Bool SAL_CALL getAllowMacroExecution() override; + + // XScriptInvocationContext + virtual css::uno::Reference< css::document::XEmbeddedScripts > SAL_CALL getScriptContainer() override; + + // XScriptProviderSupplier + virtual css::uno::Reference< css::script::provider::XScriptProvider > SAL_CALL getScriptProvider( ) override; + + // XEventsSupplier + virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; + + // XLoadable + virtual void SAL_CALL initNew( ) override; + virtual void SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + + // css.document.XDocumentRecovery + virtual sal_Bool SAL_CALL wasModifiedSinceLastSave() override; + virtual void SAL_CALL storeToRecoveryFile( const OUString& i_TargetLocation, const css::uno::Sequence< css::beans::PropertyValue >& i_MediaDescriptor ) override; + virtual void SAL_CALL recoverFromFile( const OUString& i_SourceLocation, const OUString& i_SalvagedFile, const css::uno::Sequence< css::beans::PropertyValue >& i_MediaDescriptor ) override; + + // XTitle + virtual OUString SAL_CALL getTitle( ) override; + virtual void SAL_CALL setTitle( const OUString& sTitle ) override; + + // XTitleChangeBroadcaster + virtual void SAL_CALL addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) override; + virtual void SAL_CALL removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) override; + + // XUntitledNumbers + virtual ::sal_Int32 SAL_CALL leaseNumber( const css::uno::Reference< css::uno::XInterface >& xComponent ) override; + virtual void SAL_CALL releaseNumber( ::sal_Int32 nNumber ) override; + virtual void SAL_CALL releaseNumberForComponent( const css::uno::Reference< css::uno::XInterface >& xComponent ) override; + virtual OUString SAL_CALL getUntitledPrefix( ) override; + + /** clears the given object container + + Clearing is done via disposal - the method calls XComponent::dispose at the given object, + which must be one of our impl's or our object containers (m_xForms, m_xReports, + m_xTableDefinitions, m_xCommandDefinitions) + + @param _rxContainer + the container to clear + */ + static void clearObjectContainer( + css::uno::WeakReference< css::container::XNameAccess >& _rxContainer); + + /** checks whether the component is already initialized, throws a NotInitializedException if not + */ + void checkInitialized() const + { + if ( !impl_isInitialized() ) + throw css::lang::NotInitializedException( OUString(), getThis() ); + } + + /** checks the document is currently in the initialization phase, or already initialized. + Throws NotInitializedException if not so. + */ + void checkNotUninitialized() const + { + if ( impl_isInitialized() || impl_isInitializing() ) + // fine + return; + + throw css::lang::NotInitializedException( OUString(), getThis() ); + } + + /** checks whether the document is currently being initialized, or already initialized, + throws a DoubleInitializationException if so + */ + void checkNotInitialized() const + { + if ( impl_isInitializing() || impl_isInitialized() ) + throw css::frame::DoubleInitializationException( OUString(), getThis() ); + } + +private: + /// @throws css::uno::RuntimeException + css::uno::Reference< css::ui::XUIConfigurationManager2 > const & getUIConfigurationManager2(); + + /** returns whether the model is currently being initialized + */ + bool impl_isInitializing() const { return m_eInitState == Initializing; } + + /** returns whether the model is already initialized, i.e. the XModel's "initNew" or "load" methods have been called + */ + bool impl_isInitialized() const { return m_eInitState == Initialized; } + + /// tells the model it is being initialized now + void impl_setInitializing() { m_eInitState = Initializing; } + + /// tells the model its initialization is done + void impl_setInitialized(); + + /** closes the frames of all connected controllers + + @param _bDeliverOwnership + determines if the ownership should be transferred to the component which + possibly vetos the closing + + @throws css::util::CloseVetoException + if the closing was vetoed by any instance + */ + void impl_closeControllerFrames_nolck_throw( bool _bDeliverOwnership ); + + /** disposes the frames of all controllers which are still left in m_aControllers. + */ + void impl_disposeControllerFrames_nothrow(); + + /** does a reparenting at the given object container to ourself + + Calls XChild::setParent at the given object, which must be one of our impl's or our + object containers (m_xForms, m_xReports, m_xTableDefinitions, m_xCommandDefinitions) + */ + void impl_reparent_nothrow( const css::uno::WeakReference< css::container::XNameAccess >& _rxContainer ); + + /** retrieves the forms or reports contained, creates and initializes it, if necessary + + @throws DisposedException + if the instance is already disposed + @throws IllegalArgumentException + if _eType is not ODatabaseModelImpl::E_FORM and not ODatabaseModelImpl::E_REPORT + */ + css::uno::Reference< css::container::XNameAccess > + impl_getDocumentContainer_throw( ODatabaseModelImpl::ObjectType _eType ); + + /** resets everything + + @precond + m_pImpl is not + */ + void + impl_reset_nothrow(); + + /** imports the document from the given resource. + */ + static void + impl_import_nolck_throw( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::uno::XInterface >& _rxTargetComponent, + const ::comphelper::NamedValueCollection& _rResource + ); + + /** creates a storage for the given URL, truncating it if a file with this name already exists + + @throws Exception + if creating the storage failed + + @return + the newly created storage for the file at the given URL + */ + css::uno::Reference< css::embed::XStorage > + impl_createStorageFor_throw( + const OUString& _rURL + ) const; + + /** Extracts storage from arguments, or creates for the given URL, truncating it if a file with + this name already exists + + @throws Exception + if creating the storage failed + + @return + the storage that is either extracted from arguments, or newly created for the file at + the given URL + */ + css::uno::Reference impl_GetStorageOrCreateFor_throw( + const ::comphelper::NamedValueCollection& _rArguments, const OUString& _rURL) const; + + /** sets our "modified" flag + + will notify all our respective listeners, if the "modified" state actually changed + + @param _bModified + the (new) flag indicating whether the document is currently modified or not + @param _rGuard + the guard for our instance. At method entry, the guard must hold the lock. At the moment + of method leave, the lock will be released. + @precond + our mutex is locked + @postcond + our mutex is not locked + */ + void impl_setModified_nothrow( bool _bModified, DocumentGuard& _rGuard ); + + /** stores the document to the given storage + + Note that the document is actually not rebased to this storage, it just stores a copy of itself + to the given target storage. + + @param _rxTargetStorage + denotes the storage to store the document into + @param _rMediaDescriptor + contains additional parameters for storing the document + @param _rDocGuard + a guard which holds the (only) lock to the document, and which will be temporarily + released where necessary (e.g. for notifications, or calling into other components) + + @throws css::uno::IllegalArgumentException + if the given storage is . + + @throws css::uno::RuntimeException + when any of the used operations throws it + + @throws css::io::IOException + when any of the used operations throws it, or any other exception occurs which is no + RuntimeException and no IOException + */ + void impl_storeToStorage_throw( + const css::uno::Reference< css::embed::XStorage >& _rxTargetStorage, + const css::uno::Sequence< css::beans::PropertyValue >& _rMediaDescriptor, + DocumentGuard& _rDocGuard + ) const; + + /** impl-version of attachResource + + @param i_rLogicalDocumentURL + denotes the logical URL of the document, to be reported by getURL/getLocation + @param i_rMediaDescriptor + denotes additional document parameters + @param _rDocGuard + is the guard which currently protects the document instance + */ + bool impl_attachResource( + const OUString& i_rLogicalDocumentURL, + const css::uno::Sequence< css::beans::PropertyValue >& i_rMediaDescriptor, + DocumentGuard& _rDocGuard + ); + + /** throws an IOException with the message as defined in the RID_STR_ERROR_WHILE_SAVING resource, wrapping + the given caught non-IOException error + */ + void impl_throwIOExceptionCausedBySave_throw( + const css::uno::Any& i_rError, + std::u16string_view i_rTargetURL + ) const; +}; + +/** an extended version of the ModelMethodGuard, which also cares for the initialization state + of the document +*/ +class DocumentGuard : private ModelMethodGuard +{ +public: + enum InitMethod_ + { + // a method which is to initialize the document + InitMethod, + }; + + enum DefaultMethod_ + { + // a default method + DefaultMethod + }; + + enum MethodUsedDuringInit_ + { + // a method which is used (externally) during the initialization phase + MethodUsedDuringInit + }; + + enum MethodWithoutInit_ + { + // a method which does not need initialization - use with care! + MethodWithoutInit + }; + + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::lang::NotInitializedException + if the given component is not yet initialized + */ + DocumentGuard(const ODatabaseDocument& _document, DefaultMethod_) + : ModelMethodGuard(_document) + , m_document(_document ) + { + m_document.checkInitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::frame::DoubleInitializationException + if the given component is already initialized, or currently being initialized. + */ + DocumentGuard(const ODatabaseDocument& _document, InitMethod_) + : ModelMethodGuard(_document) + , m_document(_document) + { + m_document.checkNotInitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + + @throws css::lang::NotInitializedException + if the component is still uninitialized, and not in the initialization + phase currently. + */ + DocumentGuard(const ODatabaseDocument& _document, MethodUsedDuringInit_) + : ModelMethodGuard(_document) + , m_document(_document) + { + m_document.checkNotUninitialized(); + } + + /** constructs the guard + + @param _document + the ODatabaseDocument instance + + @throws css::lang::DisposedException + If the given component is already disposed + */ + DocumentGuard(const ODatabaseDocument& _document, MethodWithoutInit_) + : ModelMethodGuard( _document ) + , m_document( _document ) + { + } + + void clear() + { + ModelMethodGuard::clear(); + } + void reset() + { + ModelMethodGuard::reset(); + m_document.checkDisposed(); + } + +private: + + const ODatabaseDocument& m_document; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databaseregistrations.cxx b/dbaccess/source/core/dataaccess/databaseregistrations.cxx new file mode 100644 index 000000000..40fa526d1 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databaseregistrations.cxx @@ -0,0 +1,369 @@ +/* -*- 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "databaseregistrations.hxx" + +namespace dbaccess +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::lang::IllegalAccessException; + using ::com::sun::star::container::ElementExistException; + using ::com::sun::star::sdb::XDatabaseRegistrations; + using ::com::sun::star::sdb::XDatabaseRegistrationsListener; + using ::com::sun::star::sdb::DatabaseRegistrationEvent; + using ::com::sun::star::uno::XAggregation; + + static OUString getConfigurationRootPath() + { + return "org.openoffice.Office.DataAccess/RegisteredNames"; + } + + static OUString getLocationNodeName() + { + return "Location"; + } + + static OUString getNameNodeName() + { + return "Name"; + } + + // DatabaseRegistrations - declaration + typedef ::cppu::WeakAggImplHelper1 < XDatabaseRegistrations + > DatabaseRegistrations_Base; + + namespace { + + class DatabaseRegistrations :public ::cppu::BaseMutex + ,public DatabaseRegistrations_Base + { + public: + explicit DatabaseRegistrations( const Reference& _rxContext ); + + protected: + virtual ~DatabaseRegistrations() override; + + public: + virtual sal_Bool SAL_CALL hasRegisteredDatabase( const OUString& Name ) override; + virtual Sequence< OUString > SAL_CALL getRegistrationNames() override; + virtual OUString SAL_CALL getDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL registerDatabaseLocation( const OUString& Name, const OUString& Location ) override; + virtual void SAL_CALL revokeDatabaseLocation( const OUString& Name ) override; + virtual void SAL_CALL changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) override; + virtual sal_Bool SAL_CALL isDatabaseRegistrationReadOnly( const OUString& Name ) override; + virtual void SAL_CALL addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override; + virtual void SAL_CALL removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override; + + private: + void + impl_checkValidName_common(std::u16string_view _rName); + ::utl::OConfigurationNode + impl_checkValidName_throw_must_exist(const OUString& _rName); + ::utl::OConfigurationNode + impl_checkValidName_throw_must_not_exist(const OUString& _rName); + + void impl_checkValidLocation_throw( std::u16string_view _rLocation ); + + /** retrieves the configuration node whose "Name" sub node has the given value + + Since we separated the name of the registration node from the "Name" value of the registration, we cannot + simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name. + Instead, we must search all nodes. + + If a node with the given display name does not exist, then a NoSuchElementException is thrown. + + If no exception is thrown, then a valid node is returned: If the node existed it is returned. + */ + ::utl::OConfigurationNode + impl_getNodeForName_throw_must_exist(const OUString& _rName); + + /** retrieves the configuration node whose "Name" sub node has the given value + + Since we separated the name of the registration node from the "Name" value of the registration, we cannot + simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name. + Instead, we must search all nodes. + + If a node with the given name already exists, then an ElementExistException is thrown. + + If no exception is thrown, then a valid node is returned: If the node did not yet exist a new node is created, + in this case the root node is not yet committed. + */ + ::utl::OConfigurationNode + impl_getNodeForName_throw_must_not_exist(const OUString& _rName); + + + ::utl::OConfigurationNode + impl_getNodeForName_nothrow(std::u16string_view _rName); + + private: + Reference m_aContext; + ::utl::OConfigurationTreeRoot m_aConfigurationRoot; + ::comphelper::OInterfaceContainerHelper3 m_aRegistrationListeners; + }; + + } + + // DatabaseRegistrations - implementation + DatabaseRegistrations::DatabaseRegistrations( const Reference & _rxContext ) + :m_aContext( _rxContext ) + ,m_aRegistrationListeners( m_aMutex ) + { + m_aConfigurationRoot = ::utl::OConfigurationTreeRoot::createWithComponentContext( + m_aContext, getConfigurationRootPath() ); + } + + DatabaseRegistrations::~DatabaseRegistrations() + { + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_nothrow( std::u16string_view _rName ) + { + const Sequence< OUString > aNames( m_aConfigurationRoot.getNodeNames() ); + for ( auto const & nodeName : aNames ) + { + ::utl::OConfigurationNode aNodeForName = m_aConfigurationRoot.openNode( nodeName ); + + OUString sTestName; + OSL_VERIFY( aNodeForName.getNodeValue( getNameNodeName() ) >>= sTestName ); + if ( sTestName == _rName ) + return aNodeForName; + } + return ::utl::OConfigurationNode(); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_exist(const OUString& _rName) + { + ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) ); + + if (!aNodeForName.isValid()) + { + throw NoSuchElementException( _rName, *this ); + } + + return aNodeForName; + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_not_exist(const OUString& _rName) + { + ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) ); + + if (aNodeForName.isValid()) + throw ElementExistException( _rName, *this ); + + // make unique + OUString sNewNodeName = "org.openoffice." + _rName; + while ( m_aConfigurationRoot.hasByName( sNewNodeName ) ) + { + sNewNodeName = "org.openoffice." + _rName + " 2"; + } + + ::utl::OConfigurationNode aNewNode( m_aConfigurationRoot.createNode( sNewNodeName ) ); + aNewNode.setNodeValue( getNameNodeName(), Any( _rName ) ); + return aNewNode; + } + + void DatabaseRegistrations::impl_checkValidName_common(std::u16string_view _rName) + { + if ( !m_aConfigurationRoot.isValid() ) + throw RuntimeException( OUString(), *this ); + + if ( _rName.empty() ) + throw IllegalArgumentException( OUString(), *this, 1 ); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_exist(const OUString& _rName) + { + impl_checkValidName_common(_rName); + return impl_getNodeForName_throw_must_exist(_rName); + } + + ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_not_exist(const OUString& _rName) + { + impl_checkValidName_common(_rName); + return impl_getNodeForName_throw_must_not_exist(_rName); + } + + void DatabaseRegistrations::impl_checkValidLocation_throw( std::u16string_view _rLocation ) + { + if ( _rLocation.empty() ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + INetURLObject aURL( _rLocation ); + if ( aURL.GetProtocol() == INetProtocol::NotValid ) + throw IllegalArgumentException( OUString(), *this, 2 ); + } + + sal_Bool SAL_CALL DatabaseRegistrations::hasRegisteredDatabase( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::utl::OConfigurationNode aNodeForName = impl_getNodeForName_nothrow( Name ); + return aNodeForName.isValid(); + } + + Sequence< OUString > SAL_CALL DatabaseRegistrations::getRegistrationNames() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_aConfigurationRoot.isValid() ) + throw RuntimeException( OUString(), *this ); + + const Sequence< OUString > aProgrammaticNames( m_aConfigurationRoot.getNodeNames() ); + Sequence< OUString > aDisplayNames( aProgrammaticNames.getLength() ); + OUString* pDisplayName = aDisplayNames.getArray(); + + for ( auto const & name : aProgrammaticNames ) + { + ::utl::OConfigurationNode aRegistrationNode = m_aConfigurationRoot.openNode( name ); + OSL_VERIFY( aRegistrationNode.getNodeValue( getNameNodeName() ) >>= *pDisplayName ); + ++pDisplayName; + } + + return aDisplayNames; + } + + OUString SAL_CALL DatabaseRegistrations::getDatabaseLocation( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name); + + OUString sLocation; + OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation ); + sLocation = SvtPathOptions().SubstituteVariable( sLocation ); + + return sLocation; + } + + void SAL_CALL DatabaseRegistrations::registerDatabaseLocation( const OUString& Name, const OUString& Location ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + impl_checkValidLocation_throw( Location ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_not_exist(Name); + + // register + aDataSourceRegistration.setNodeValue( getLocationNodeName(), Any( Location ) ); + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, OUString(), Location ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::registeredDatabaseLocation, aEvent ); + } + + void SAL_CALL DatabaseRegistrations::revokeDatabaseLocation( const OUString& Name ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name); + + // obtain properties for notification + OUString sLocation; + OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation ); + + // revoke + if ( aNodeForName.isReadonly() + || !m_aConfigurationRoot.removeNode( aNodeForName.getLocalName() ) + ) + throw IllegalAccessException( OUString(), *this ); + + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, sLocation, OUString() ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::revokedDatabaseLocation, aEvent ); + } + + void SAL_CALL DatabaseRegistrations::changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + // check + impl_checkValidLocation_throw( NewLocation ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name); + + if ( aDataSourceRegistration.isReadonly() ) + throw IllegalAccessException( OUString(), *this ); + + // obtain properties for notification + OUString sOldLocation; + OSL_VERIFY( aDataSourceRegistration.getNodeValue( getLocationNodeName() ) >>= sOldLocation ); + + // change + aDataSourceRegistration.setNodeValue( getLocationNodeName(), Any( NewLocation ) ); + m_aConfigurationRoot.commit(); + + // notify + DatabaseRegistrationEvent aEvent( *this, Name, sOldLocation, NewLocation ); + aGuard.clear(); + m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::changedDatabaseLocation, aEvent ); + } + + sal_Bool SAL_CALL DatabaseRegistrations::isDatabaseRegistrationReadOnly( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name); + return aDataSourceRegistration.isReadonly(); + } + + void SAL_CALL DatabaseRegistrations::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) + { + if ( Listener.is() ) + m_aRegistrationListeners.addInterface( Listener ); + } + + void SAL_CALL DatabaseRegistrations::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) + { + if ( Listener.is() ) + m_aRegistrationListeners.removeInterface( Listener ); + } + + // DatabaseRegistrations - factory + Reference< XAggregation > createDataSourceRegistrations( const Reference & _rxContext ) + { + return new DatabaseRegistrations( _rxContext ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/databaseregistrations.hxx b/dbaccess/source/core/dataaccess/databaseregistrations.hxx new file mode 100644 index 000000000..709bc9f98 --- /dev/null +++ b/dbaccess/source/core/dataaccess/databaseregistrations.hxx @@ -0,0 +1,32 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include + +namespace dbaccess +{ +css::uno::Reference +createDataSourceRegistrations(const css::uno::Reference& _rxContext); + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/datasource.cxx b/dbaccess/source/core/dataaccess/datasource.cxx new file mode 100644 index 000000000..d88b661c9 --- /dev/null +++ b/dbaccess/source/core/dataaccess/datasource.cxx @@ -0,0 +1,1437 @@ +/* -*- 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 "datasource.hxx" +#include "commandcontainer.hxx" +#include +#include +#include +#include +#include "connection.hxx" +#include "SharedConnection.hxx" +#include "databasedocument.hxx" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::reflection; +using namespace ::cppu; +using namespace ::osl; +using namespace ::dbtools; +using namespace ::comphelper; + +namespace dbaccess +{ + +namespace { + +/** helper class which implements a XFlushListener, and forwards all + notification events to another XFlushListener + + The speciality is that the foreign XFlushListener instance, to which + the notifications are forwarded, is held weak. + + Thus, the class can be used with XFlushable instance which hold + their listeners with a hard reference, if you simply do not *want* + to be held hard-ref-wise. +*/ +class FlushNotificationAdapter : public ::cppu::WeakImplHelper< XFlushListener > +{ +private: + WeakReference< XFlushable > m_aBroadcaster; + WeakReference< XFlushListener > m_aListener; + +public: + static void installAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ) + { + new FlushNotificationAdapter( _rxBroadcaster, _rxListener ); + } + +protected: + FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ); + virtual ~FlushNotificationAdapter() override; + + void impl_dispose(); + +protected: + // XFlushListener + virtual void SAL_CALL flushed( const css::lang::EventObject& rEvent ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +}; + +} + +FlushNotificationAdapter::FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener ) + :m_aBroadcaster( _rxBroadcaster ) + ,m_aListener( _rxListener ) +{ + OSL_ENSURE( _rxBroadcaster.is(), "FlushNotificationAdapter::FlushNotificationAdapter: invalid flushable!" ); + + osl_atomic_increment( &m_refCount ); + { + if ( _rxBroadcaster.is() ) + _rxBroadcaster->addFlushListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount == 1, "FlushNotificationAdapter::FlushNotificationAdapter: broadcaster isn't holding by hard ref!?" ); +} + +FlushNotificationAdapter::~FlushNotificationAdapter() +{ +} + +void FlushNotificationAdapter::impl_dispose() +{ + Reference< XFlushListener > xKeepAlive( this ); + + Reference< XFlushable > xFlushable( m_aBroadcaster ); + if ( xFlushable.is() ) + xFlushable->removeFlushListener( this ); + + m_aListener.clear(); + m_aBroadcaster.clear(); +} + +void SAL_CALL FlushNotificationAdapter::flushed( const EventObject& rEvent ) +{ + Reference< XFlushListener > xListener( m_aListener ); + if ( xListener.is() ) + xListener->flushed( rEvent ); + else + impl_dispose(); +} + +void SAL_CALL FlushNotificationAdapter::disposing( const EventObject& Source ) +{ + Reference< XFlushListener > xListener( m_aListener ); + if ( xListener.is() ) + xListener->disposing( Source ); + + impl_dispose(); +} + +OAuthenticationContinuation::OAuthenticationContinuation() + :m_bRememberPassword(true), // TODO: a meaningful default + m_bCanSetUserName(true) +{ +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetRealm( ) +{ + return false; +} + +void SAL_CALL OAuthenticationContinuation::setRealm( const OUString& /*Realm*/ ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setRealm: not supported!"); +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetUserName( ) +{ + // we always allow this, even if the database document is read-only. In this case, + // it's simply that the user cannot store the new user name. + return m_bCanSetUserName; +} + +void SAL_CALL OAuthenticationContinuation::setUserName( const OUString& _rUser ) +{ + m_sUser = _rUser; +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetPassword( ) +{ + return true; +} + +void SAL_CALL OAuthenticationContinuation::setPassword( const OUString& _rPassword ) +{ + m_sPassword = _rPassword; +} + +Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberPasswordModes( RememberAuthentication& _reDefault ) +{ + _reDefault = RememberAuthentication_SESSION; + return { _reDefault }; +} + +void SAL_CALL OAuthenticationContinuation::setRememberPassword( RememberAuthentication _eRemember ) +{ + m_bRememberPassword = (RememberAuthentication_NO != _eRemember); +} + +sal_Bool SAL_CALL OAuthenticationContinuation::canSetAccount( ) +{ + return false; +} + +void SAL_CALL OAuthenticationContinuation::setAccount( const OUString& ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setAccount: not supported!"); +} + +Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberAccountModes( RememberAuthentication& _reDefault ) +{ + _reDefault = RememberAuthentication_NO; + return { RememberAuthentication_NO }; +} + +void SAL_CALL OAuthenticationContinuation::setRememberAccount( RememberAuthentication /*Remember*/ ) +{ + SAL_WARN("dbaccess","OAuthenticationContinuation::setRememberAccount: not supported!"); +} + +namespace { + +/** The class OSharedConnectionManager implements a structure to share connections. + It owns the master connections which will be disposed when the last connection proxy is gone. +*/ +// need to hold the digest +struct TDigestHolder +{ + sal_uInt8 m_pBuffer[RTL_DIGEST_LENGTH_SHA1]; + TDigestHolder() + { + m_pBuffer[0] = 0; + } + +}; + +} + +class OSharedConnectionManager : public ::cppu::WeakImplHelper< XEventListener > +{ + + // contains the currently used master connections + struct TConnectionHolder + { + Reference< XConnection > xMasterConnection; + oslInterlockedCount nALiveCount; + }; + + // the less-compare functor, used for the stl::map + struct TDigestLess + { + bool operator() (const TDigestHolder& x, const TDigestHolder& y) const + { + sal_uInt32 i; + for(i=0;i < RTL_DIGEST_LENGTH_SHA1 && (x.m_pBuffer[i] >= y.m_pBuffer[i]); ++i) + ; + return i < RTL_DIGEST_LENGTH_SHA1; + } + }; + + typedef std::map< TDigestHolder,TConnectionHolder,TDigestLess> TConnectionMap; // holds the master connections + typedef std::map< Reference< XConnection >,TConnectionMap::iterator> TSharedConnectionMap;// holds the shared connections + + ::osl::Mutex m_aMutex; + TConnectionMap m_aConnections; // remember the master connection in conjunction with the digest + TSharedConnectionMap m_aSharedConnection; // the shared connections with conjunction with an iterator into the connections map + Reference< XProxyFactory > m_xProxyFactory; + +protected: + virtual ~OSharedConnectionManager() override; + +public: + explicit OSharedConnectionManager(const Reference< XComponentContext >& _rxContext); + + void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + Reference getConnection( const OUString& url, + const OUString& user, + const OUString& password, + const Sequence< PropertyValue >& _aInfo, + ODatabaseSource* _pDataSource); + void addEventListener(const Reference& _rxConnection, TConnectionMap::iterator const & _rIter); +}; + +OSharedConnectionManager::OSharedConnectionManager(const Reference< XComponentContext >& _rxContext) +{ + m_xProxyFactory.set( ProxyFactory::create( _rxContext ) ); +} + +OSharedConnectionManager::~OSharedConnectionManager() +{ +} + +void SAL_CALL OSharedConnectionManager::disposing( const css::lang::EventObject& Source ) +{ + MutexGuard aGuard(m_aMutex); + Reference xConnection(Source.Source,UNO_QUERY); + TSharedConnectionMap::const_iterator aFind = m_aSharedConnection.find(xConnection); + if ( m_aSharedConnection.end() != aFind ) + { + osl_atomic_decrement(&aFind->second->second.nALiveCount); + if ( !aFind->second->second.nALiveCount ) + { + ::comphelper::disposeComponent(aFind->second->second.xMasterConnection); + m_aConnections.erase(aFind->second); + } + m_aSharedConnection.erase(aFind); + } +} + +Reference OSharedConnectionManager::getConnection( const OUString& url, + const OUString& user, + const OUString& password, + const Sequence< PropertyValue >& _aInfo, + ODatabaseSource* _pDataSource) +{ + MutexGuard aGuard(m_aMutex); + TConnectionMap::key_type nId; + Sequence< PropertyValue > aInfoCopy(_aInfo); + sal_Int32 nPos = aInfoCopy.getLength(); + aInfoCopy.realloc( nPos + 2 ); + auto pInfoCopy = aInfoCopy.getArray(); + pInfoCopy[nPos].Name = "TableFilter"; + pInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableFilter; + pInfoCopy[nPos].Name = "TableTypeFilter"; + pInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableTypeFilter; + + OUString sUser = user; + OUString sPassword = password; + if ((sUser.isEmpty()) && (sPassword.isEmpty()) && (!_pDataSource->m_pImpl->m_sUser.isEmpty())) + { // ease the usage of this method. data source which are intended to have a user automatically + // fill in the user/password combination if the caller of this method does not specify otherwise + sUser = _pDataSource->m_pImpl->m_sUser; + if (!_pDataSource->m_pImpl->m_aPassword.isEmpty()) + sPassword = _pDataSource->m_pImpl->m_aPassword; + } + + ::connectivity::OConnectionWrapper::createUniqueId(url,aInfoCopy,nId.m_pBuffer,sUser,sPassword); + TConnectionMap::iterator aIter = m_aConnections.find(nId); + + if ( m_aConnections.end() == aIter ) + { + TConnectionHolder aHolder; + aHolder.nALiveCount = 0; // will be incremented by addListener + aHolder.xMasterConnection = _pDataSource->buildIsolatedConnection(user,password); + aIter = m_aConnections.emplace(nId,aHolder).first; + } + + Reference xRet; + if ( aIter->second.xMasterConnection.is() ) + { + Reference< XAggregation > xConProxy = m_xProxyFactory->createProxy(aIter->second.xMasterConnection); + xRet = new OSharedConnection(xConProxy); + m_aSharedConnection.emplace(xRet,aIter); + addEventListener(xRet,aIter); + } + + return xRet; +} + +void OSharedConnectionManager::addEventListener(const Reference& _rxConnection, TConnectionMap::iterator const & _rIter) +{ + Reference xComp(_rxConnection,UNO_QUERY); + xComp->addEventListener(this); + OSL_ENSURE( m_aConnections.end() != _rIter , "Iterator is end!"); + osl_atomic_increment(&_rIter->second.nALiveCount); +} + +namespace +{ + Sequence< PropertyValue > lcl_filterDriverProperties( const Reference< XDriver >& _xDriver, const OUString& _sUrl, + const Sequence< PropertyValue >& _rDataSourceSettings, const AsciiPropertyValue* _pKnownSettings ) + { + if ( _xDriver.is() ) + { + Sequence< DriverPropertyInfo > aDriverInfo(_xDriver->getPropertyInfo(_sUrl,_rDataSourceSettings)); + + const PropertyValue* pDataSourceSetting = _rDataSourceSettings.getConstArray(); + const PropertyValue* pEnd = pDataSourceSetting + _rDataSourceSettings.getLength(); + + std::vector< PropertyValue > aRet; + + for ( ; pDataSourceSetting != pEnd ; ++pDataSourceSetting ) + { + bool bAllowSetting = false; + const AsciiPropertyValue* pSetting = _pKnownSettings; + for ( ; pSetting->AsciiName; ++pSetting ) + { + if ( pDataSourceSetting->Name.equalsAscii( pSetting->AsciiName ) ) + { // the particular data source setting is known + + const DriverPropertyInfo* pAllowedDriverSetting = aDriverInfo.getConstArray(); + const DriverPropertyInfo* pDriverSettingsEnd = pAllowedDriverSetting + aDriverInfo.getLength(); + for ( ; pAllowedDriverSetting != pDriverSettingsEnd; ++pAllowedDriverSetting ) + { + if ( pAllowedDriverSetting->Name.equalsAscii( pSetting->AsciiName ) ) + { // the driver also allows this setting + bAllowSetting = true; + break; + } + } + break; + } + } + if ( bAllowSetting || !pSetting->AsciiName ) + { // if the driver allows this particular setting, or if the setting is completely unknown, + // we pass it to the driver + aRet.push_back( *pDataSourceSetting ); + } + } + if ( !aRet.empty() ) + return comphelper::containerToSequence(aRet); + } + return Sequence< PropertyValue >(); + } + + typedef std::map< OUString, sal_Int32 > PropertyAttributeCache; + + struct IsDefaultAndNotRemoveable + { + private: + const PropertyAttributeCache& m_rAttribs; + + public: + explicit IsDefaultAndNotRemoveable( const PropertyAttributeCache& _rAttribs ) : m_rAttribs( _rAttribs ) { } + + bool operator()( const PropertyValue& _rProp ) + { + if ( _rProp.State != PropertyState_DEFAULT_VALUE ) + return false; + + bool bRemoveable = true; + + PropertyAttributeCache::const_iterator pos = m_rAttribs.find( _rProp.Name ); + OSL_ENSURE( pos != m_rAttribs.end(), "IsDefaultAndNotRemoveable: illegal property name!" ); + if ( pos != m_rAttribs.end() ) + bRemoveable = ( ( pos->second & PropertyAttribute::REMOVABLE ) != 0 ); + + return !bRemoveable; + } + }; +} + + +ODatabaseSource::ODatabaseSource(const ::rtl::Reference& _pImpl) + :ModelDependentComponent( _pImpl ) + ,ODatabaseSource_Base( getMutex() ) + ,OPropertySetHelper( ODatabaseSource_Base::rBHelper ) + , m_Bookmarks(*this, getMutex()) + ,m_aFlushListeners( getMutex() ) +{ + // some kind of default + SAL_INFO("dbaccess", "DS: ctor: " << std::hex << this << ": " << std::hex << m_pImpl.get() ); +} + +ODatabaseSource::~ODatabaseSource() +{ + SAL_INFO("dbaccess", "DS: dtor: " << std::hex << this << ": " << std::hex << m_pImpl.get() ); + if ( !ODatabaseSource_Base::rBHelper.bInDispose && !ODatabaseSource_Base::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +void ODatabaseSource::setName( const Reference< XDocumentDataSource >& _rxDocument, const OUString& _rNewName, DBContextAccess ) +{ + ODatabaseSource& rModelImpl = dynamic_cast< ODatabaseSource& >( *_rxDocument ); + + SolarMutexGuard g; + if ( rModelImpl.m_pImpl.is() ) + rModelImpl.m_pImpl->m_sName = _rNewName; +} + +// css::lang::XTypeProvider +Sequence< Type > ODatabaseSource::getTypes() +{ + OTypeCollection aPropertyHelperTypes( cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ::comphelper::concatSequences( + ODatabaseSource_Base::getTypes(), + aPropertyHelperTypes.getTypes() + ); +} + +Sequence< sal_Int8 > ODatabaseSource::getImplementationId() +{ + return css::uno::Sequence(); +} + +// css::uno::XInterface +Any ODatabaseSource::queryInterface( const Type & rType ) +{ + Any aIface = ODatabaseSource_Base::queryInterface( rType ); + if ( !aIface.hasValue() ) + aIface = ::cppu::OPropertySetHelper::queryInterface( rType ); + return aIface; +} + +void ODatabaseSource::acquire() noexcept +{ + ODatabaseSource_Base::acquire(); +} + +void ODatabaseSource::release() noexcept +{ + ODatabaseSource_Base::release(); +} + +void SAL_CALL ODatabaseSource::disposing( const css::lang::EventObject& Source ) +{ + if ( m_pImpl.is() ) + m_pImpl->disposing(Source); +} + +// XServiceInfo +OUString ODatabaseSource::getImplementationName( ) +{ + return "com.sun.star.comp.dba.ODatabaseSource"; +} + +Sequence< OUString > ODatabaseSource::getSupportedServiceNames( ) +{ + return { SERVICE_SDB_DATASOURCE, "com.sun.star.sdb.DocumentDataSource" }; +} + +sal_Bool ODatabaseSource::supportsService( const OUString& _rServiceName ) +{ + return cppu::supportsService(this, _rServiceName); +} + +// OComponentHelper +void ODatabaseSource::disposing() +{ + SAL_INFO("dbaccess", "DS: disp: " << std::hex << this << ", " << std::hex << m_pImpl.get() ); + ODatabaseSource_Base::WeakComponentImplHelperBase::disposing(); + OPropertySetHelper::disposing(); + + EventObject aDisposeEvent(static_cast(this)); + m_aFlushListeners.disposeAndClear( aDisposeEvent ); + + ODatabaseDocument::clearObjectContainer(m_pImpl->m_xCommandDefinitions); + ODatabaseDocument::clearObjectContainer(m_pImpl->m_xTableDefinitions); + m_pImpl.clear(); +} + +weld::Window* ODatabaseModelImpl::GetFrameWeld() +{ + if (m_xDialogParent.is()) + return Application::GetFrameWeld(m_xDialogParent); + + Reference xModel = getModel_noCreate(); + if (!xModel.is()) + return nullptr; + Reference xController(xModel->getCurrentController()); + if (!xController.is()) + return nullptr; + Reference xFrame(xController->getFrame()); + if (!xFrame.is()) + return nullptr; + Reference xWindow(xFrame->getContainerWindow()); + return Application::GetFrameWeld(xWindow); +} + +Reference< XConnection > ODatabaseSource::buildLowLevelConnection(const OUString& _rUid, const OUString& _rPwd) +{ + Reference< XConnection > xReturn; + + Reference< XDriverManager > xManager; + +#if ENABLE_FIREBIRD_SDBC + bool bIgnoreMigration = false; + bool bNeedMigration = false; + Reference< XModel > xModel = m_pImpl->getModel_noCreate(); + if ( xModel) + { + //See ODbTypeWizDialogSetup::SaveDatabaseDocument + ::comphelper::NamedValueCollection::get(xModel->getArgs(), u"IgnoreFirebirdMigration") >>= bIgnoreMigration; + } + else + { + //ignore when we don't have a model. E.g. Mailmerge, data sources, fields... + bIgnoreMigration = true; + } + + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + bIgnoreMigration = true; + + if(!bIgnoreMigration && m_pImpl->m_sConnectURL == "sdbc:embedded:hsqldb") + { + Reference const xRootStorage = m_pImpl->getOrCreateRootStorage(); + OUString sMigrEnvVal; + osl_getEnvironment(OUString("DBACCESS_HSQL_MIGRATION").pData, + &sMigrEnvVal.pData); + if(!sMigrEnvVal.isEmpty()) + bNeedMigration = true; + else + { + Reference const xPropSet(xRootStorage, UNO_QUERY_THROW); + sal_Int32 nOpenMode(0); + if ((xPropSet->getPropertyValue("OpenMode") >>= nOpenMode) + && (nOpenMode & css::embed::ElementModes::WRITE) + && (!Application::IsHeadlessModeEnabled())) + { + MigrationWarnDialog aWarnDlg(m_pImpl->GetFrameWeld()); + bNeedMigration = aWarnDlg.run() == RET_OK; + } + } + if (bNeedMigration) + { + // back up content xml file if migration was successful + static constexpr OUStringLiteral BACKUP_XML_NAME = u"content_before_migration.xml"; + try + { + if(xRootStorage->isStreamElement(BACKUP_XML_NAME)) + xRootStorage->removeElement(BACKUP_XML_NAME); + } + catch (NoSuchElementException&) + { + SAL_INFO("dbaccess", "No file content_before_migration.xml found" ); + } + xRootStorage->copyElementTo("content.xml", xRootStorage, + BACKUP_XML_NAME); + + m_pImpl->m_sConnectURL = "sdbc:embedded:firebird"; + } + } +#endif + + try { + xManager.set( ConnectionPool::create( m_pImpl->m_aContext ), UNO_QUERY_THROW ); + } catch( const Exception& ) { } + if ( !xManager.is() ) + // no connection pool installed, fall back to driver manager + xManager.set( DriverManager::create(m_pImpl->m_aContext ), UNO_QUERY_THROW ); + + OUString sUser(_rUid); + OUString sPwd(_rPwd); + if ((sUser.isEmpty()) && (sPwd.isEmpty()) && (!m_pImpl->m_sUser.isEmpty())) + { // ease the usage of this method. data source which are intended to have a user automatically + // fill in the user/password combination if the caller of this method does not specify otherwise + sUser = m_pImpl->m_sUser; + if (!m_pImpl->m_aPassword.isEmpty()) + sPwd = m_pImpl->m_aPassword; + } + + TranslateId pExceptionMessageId = RID_STR_COULDNOTCONNECT_UNSPECIFIED; + if (xManager.is()) + { + sal_Int32 nAdditionalArgs(0); + if (!sUser.isEmpty()) ++nAdditionalArgs; + if (!sPwd.isEmpty()) ++nAdditionalArgs; + + Sequence< PropertyValue > aUserPwd(nAdditionalArgs); + auto aUserPwdRange = asNonConstRange(aUserPwd); + sal_Int32 nArgPos = 0; + if (!sUser.isEmpty()) + { + aUserPwdRange[ nArgPos ].Name = "user"; + aUserPwdRange[ nArgPos ].Value <<= sUser; + ++nArgPos; + } + if (!sPwd.isEmpty()) + { + aUserPwdRange[ nArgPos ].Name = "password"; + aUserPwdRange[ nArgPos ].Value <<= sPwd; + } + Reference< XDriver > xDriver; + try + { + + // choose driver + Reference< XDriverAccess > xAccessDrivers( xManager, UNO_QUERY ); + if ( xAccessDrivers.is() ) + xDriver = xAccessDrivers->getDriverByURL( m_pImpl->m_sConnectURL ); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("dbaccess", "ODatabaseSource::buildLowLevelConnection: got a strange exception while analyzing the error" ); + } + if ( !xDriver.is() || !xDriver->acceptsURL( m_pImpl->m_sConnectURL ) ) + { + // Nowadays, it's allowed for a driver to be registered for a given URL, but actually not to accept it. + // This is because registration nowadays happens at compile time (by adding respective configuration data), + // but acceptance is decided at runtime. + pExceptionMessageId = RID_STR_COULDNOTCONNECT_NODRIVER; + } + else + { + Sequence< PropertyValue > aDriverInfo = lcl_filterDriverProperties( + xDriver, + m_pImpl->m_sConnectURL, + m_pImpl->m_xSettings->getPropertyValues(), + dbaccess::ODatabaseModelImpl::getDefaultDataSourceSettings() + ); + + if ( m_pImpl->isEmbeddedDatabase() ) + { + sal_Int32 nCount = aDriverInfo.getLength(); + aDriverInfo.realloc(nCount + 3 ); + auto pDriverInfo = aDriverInfo.getArray(); + + pDriverInfo[nCount].Name = "URL"; + pDriverInfo[nCount++].Value <<= m_pImpl->getURL(); + + pDriverInfo[nCount].Name = "Storage"; + Reference< css::document::XDocumentSubStorageSupplier> xDocSup( m_pImpl->getDocumentSubStorageSupplier() ); + pDriverInfo[nCount++].Value <<= xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE); + + pDriverInfo[nCount].Name = "Document"; + pDriverInfo[nCount++].Value <<= getDatabaseDocument(); + } + if (nAdditionalArgs) + xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL, ::comphelper::concatSequences(aUserPwd,aDriverInfo)); + else + xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL,aDriverInfo); + + if ( m_pImpl->isEmbeddedDatabase() ) + { + // see ODatabaseSource::flushed for comment on why we register as FlushListener + // at the connection + Reference< XFlushable > xFlushable( xReturn, UNO_QUERY ); + if ( xFlushable.is() ) + FlushNotificationAdapter::installAdapter( xFlushable, this ); + } + } + } + else + pExceptionMessageId = RID_STR_COULDNOTLOAD_MANAGER; + + if ( !xReturn.is() ) + { + OUString sMessage = DBA_RES(pExceptionMessageId) + .replaceAll("$name$", m_pImpl->m_sConnectURL); + + SQLContext aContext; + aContext.Message = DBA_RES(RID_STR_CONNECTION_REQUEST). + replaceFirst("$name$", m_pImpl->m_sConnectURL); + + throwGenericSQLException( sMessage, static_cast< XDataSource* >( this ), Any( aContext ) ); + } + +#if ENABLE_FIREBIRD_SDBC + if( bNeedMigration ) + { + Reference< css::document::XDocumentSubStorageSupplier> xDocSup( + m_pImpl->getDocumentSubStorageSupplier() ); + dbahsql::HsqlImporter importer(xReturn, + xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE) ); + importer.importHsqlDatabase(m_pImpl->GetFrameWeld()); + } +#endif + + return xReturn; +} + +// OPropertySetHelper +Reference< XPropertySetInfo > ODatabaseSource::getPropertySetInfo() +{ + return createPropertySetInfo( getInfoHelper() ) ; +} + +// comphelper::OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* ODatabaseSource::createArrayHelper( ) const +{ + return new ::cppu::OPropertyArrayHelper + { + { + { PROPERTY_INFO, PROPERTY_ID_INFO, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISPASSWORDREQUIRED, PROPERTY_ID_ISPASSWORDREQUIRED, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_ISREADONLY, PROPERTY_ID_ISREADONLY, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_LAYOUTINFORMATION, PROPERTY_ID_LAYOUTINFORMATION, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_NAME, PROPERTY_ID_NAME, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY }, + { PROPERTY_NUMBERFORMATSSUPPLIER, PROPERTY_ID_NUMBERFORMATSSUPPLIER, cppu::UnoType::get(), + css::beans::PropertyAttribute::READONLY | css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_PASSWORD, PROPERTY_ID_PASSWORD, cppu::UnoType::get(), css::beans::PropertyAttribute::TRANSIENT }, + { PROPERTY_SETTINGS, PROPERTY_ID_SETTINGS, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY }, + { PROPERTY_SUPPRESSVERSIONCL, PROPERTY_ID_SUPPRESSVERSIONCL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLEFILTER, PROPERTY_ID_TABLEFILTER, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_TABLETYPEFILTER, PROPERTY_ID_TABLETYPEFILTER, cppu::UnoType>::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_URL, PROPERTY_ID_URL, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND }, + { PROPERTY_USER, PROPERTY_ID_USER, cppu::UnoType::get(), css::beans::PropertyAttribute::BOUND } + } + }; +} + +// cppu::OPropertySetHelper +::cppu::IPropertyArrayHelper& ODatabaseSource::getInfoHelper() +{ + return *getArrayHelper(); +} + +sal_Bool ODatabaseSource::convertFastPropertyValue(Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue ) +{ + bool bModified(false); + if ( m_pImpl.is() ) + { + switch (nHandle) + { + case PROPERTY_ID_TABLEFILTER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableFilter); + break; + case PROPERTY_ID_TABLETYPEFILTER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableTypeFilter); + break; + case PROPERTY_ID_USER: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sUser); + break; + case PROPERTY_ID_PASSWORD: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aPassword); + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bPasswordRequired); + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bSuppressVersionColumns); + break; + case PROPERTY_ID_LAYOUTINFORMATION: + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aLayoutInformation); + break; + case PROPERTY_ID_URL: + { + bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sConnectURL); + } break; + case PROPERTY_ID_INFO: + { + Sequence aValues; + if (!(rValue >>= aValues)) + throw IllegalArgumentException(); + + for ( auto const & checkName : std::as_const(aValues) ) + { + if ( checkName.Name.isEmpty() ) + throw IllegalArgumentException(); + } + + Sequence< PropertyValue > aSettings = m_pImpl->m_xSettings->getPropertyValues(); + bModified = aSettings.getLength() != aValues.getLength(); + if ( !bModified ) + { + const PropertyValue* pInfoIter = aSettings.getConstArray(); + const PropertyValue* checkValue = aValues.getConstArray(); + for ( ;!bModified && checkValue != std::cend(aValues) ; ++checkValue,++pInfoIter) + { + bModified = checkValue->Name != pInfoIter->Name; + if ( !bModified ) + { + bModified = checkValue->Value != pInfoIter->Value; + } + } + } + + rConvertedValue = rValue; + rOldValue <<= aSettings; + } + break; + default: + SAL_WARN("dbaccess", "ODatabaseSource::convertFastPropertyValue: unknown or readonly Property!" ); + } + } + return bModified; +} + +namespace +{ + struct SelectPropertyName + { + public: + const OUString& operator()( const PropertyValue& _lhs ) + { + return _lhs.Name; + } + }; + + /** sets a new set of property values for a given property bag instance + + The method takes a property bag, and a sequence of property values to set for this bag. + Upon return, every property which is not part of the given sequence is +
  • removed from the bag, if it's a removable property
  • +
  • orreset to its default value, if it's not a removable property
  • +
. + + @param _rxPropertyBag + the property bag to operate on + @param _rAllNewPropertyValues + the new property values to set for the bag + */ + void lcl_setPropertyValues_resetOrRemoveOther( const Reference< XPropertyBag >& _rxPropertyBag, const Sequence< PropertyValue >& _rAllNewPropertyValues ) + { + // sequences are ugly to operate on + std::set aToBeSetPropertyNames; + std::transform( + _rAllNewPropertyValues.begin(), + _rAllNewPropertyValues.end(), + std::inserter( aToBeSetPropertyNames, aToBeSetPropertyNames.end() ), + SelectPropertyName() + ); + + try + { + // obtain all properties currently known at the bag + Reference< XPropertySetInfo > xPSI( _rxPropertyBag->getPropertySetInfo(), UNO_SET_THROW ); + const Sequence< Property > aAllExistentProperties( xPSI->getProperties() ); + + Reference< XPropertyState > xPropertyState( _rxPropertyBag, UNO_QUERY_THROW ); + + // loop through them, and reset resp. default properties which are not to be set + for ( auto const & existentProperty : aAllExistentProperties ) + { + if ( aToBeSetPropertyNames.find( existentProperty.Name ) != aToBeSetPropertyNames.end() ) + continue; + + // this property is not to be set, but currently exists in the bag. + // -> Remove it, or reset it to the default. + if ( ( existentProperty.Attributes & PropertyAttribute::REMOVABLE ) != 0 ) + _rxPropertyBag->removeProperty( existentProperty.Name ); + else + xPropertyState->setPropertyToDefault( existentProperty.Name ); + } + + // finally, set the new property values + _rxPropertyBag->setPropertyValues( _rAllNewPropertyValues ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } +} + +void ODatabaseSource::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue ) +{ + if ( !m_pImpl.is() ) + return; + + switch(nHandle) + { + case PROPERTY_ID_TABLEFILTER: + rValue >>= m_pImpl->m_aTableFilter; + break; + case PROPERTY_ID_TABLETYPEFILTER: + rValue >>= m_pImpl->m_aTableTypeFilter; + break; + case PROPERTY_ID_USER: + rValue >>= m_pImpl->m_sUser; + // if the user name has changed, reset the password + m_pImpl->m_aPassword.clear(); + break; + case PROPERTY_ID_PASSWORD: + rValue >>= m_pImpl->m_aPassword; + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + m_pImpl->m_bPasswordRequired = any2bool(rValue); + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + m_pImpl->m_bSuppressVersionColumns = any2bool(rValue); + break; + case PROPERTY_ID_URL: + rValue >>= m_pImpl->m_sConnectURL; + break; + case PROPERTY_ID_INFO: + { + Sequence< PropertyValue > aInfo; + OSL_VERIFY( rValue >>= aInfo ); + lcl_setPropertyValues_resetOrRemoveOther( m_pImpl->m_xSettings, aInfo ); + } + break; + case PROPERTY_ID_LAYOUTINFORMATION: + rValue >>= m_pImpl->m_aLayoutInformation; + break; + } + m_pImpl->setModified(true); +} + +void ODatabaseSource::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const +{ + if ( !m_pImpl.is() ) + return; + + switch (nHandle) + { + case PROPERTY_ID_TABLEFILTER: + rValue <<= m_pImpl->m_aTableFilter; + break; + case PROPERTY_ID_TABLETYPEFILTER: + rValue <<= m_pImpl->m_aTableTypeFilter; + break; + case PROPERTY_ID_USER: + rValue <<= m_pImpl->m_sUser; + break; + case PROPERTY_ID_PASSWORD: + rValue <<= m_pImpl->m_aPassword; + break; + case PROPERTY_ID_ISPASSWORDREQUIRED: + rValue <<= m_pImpl->m_bPasswordRequired; + break; + case PROPERTY_ID_SUPPRESSVERSIONCL: + rValue <<= m_pImpl->m_bSuppressVersionColumns; + break; + case PROPERTY_ID_ISREADONLY: + rValue <<= m_pImpl->m_bReadOnly; + break; + case PROPERTY_ID_INFO: + { + try + { + // collect the property attributes of all current settings + Reference< XPropertySet > xSettingsAsProps( m_pImpl->m_xSettings, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPST( xSettingsAsProps->getPropertySetInfo(), UNO_SET_THROW ); + const Sequence< Property > aSettings( xPST->getProperties() ); + std::map< OUString, sal_Int32 > aPropertyAttributes; + for ( auto const & setting : aSettings ) + { + aPropertyAttributes[ setting.Name ] = setting.Attributes; + } + + // get all current settings with their values + Sequence< PropertyValue > aValues( m_pImpl->m_xSettings->getPropertyValues() ); + + // transform them so that only property values which fulfill certain + // criteria survive + Sequence< PropertyValue > aNonDefaultOrUserDefined( aValues.getLength() ); + auto [begin, end] = asNonConstRange(aValues); + auto pCopyStart = aNonDefaultOrUserDefined.getArray(); + const PropertyValue* pCopyEnd = std::remove_copy_if( + begin, + end, + pCopyStart, + IsDefaultAndNotRemoveable( aPropertyAttributes ) + ); + aNonDefaultOrUserDefined.realloc( pCopyEnd - pCopyStart ); + rValue <<= aNonDefaultOrUserDefined; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + break; + case PROPERTY_ID_SETTINGS: + rValue <<= m_pImpl->m_xSettings; + break; + case PROPERTY_ID_URL: + rValue <<= m_pImpl->m_sConnectURL; + break; + case PROPERTY_ID_NUMBERFORMATSSUPPLIER: + rValue <<= m_pImpl->getNumberFormatsSupplier(); + break; + case PROPERTY_ID_NAME: + rValue <<= m_pImpl->m_sName; + break; + case PROPERTY_ID_LAYOUTINFORMATION: + rValue <<= m_pImpl->m_aLayoutInformation; + break; + default: + SAL_WARN("dbaccess","unknown Property"); + } +} + +// XDataSource +void ODatabaseSource::setLoginTimeout(sal_Int32 seconds) +{ + ModelMethodGuard aGuard( *this ); + m_pImpl->m_nLoginTimeout = seconds; +} + +sal_Int32 ODatabaseSource::getLoginTimeout() +{ + ModelMethodGuard aGuard( *this ); + return m_pImpl->m_nLoginTimeout; +} + +// XCompletedConnection +Reference< XConnection > SAL_CALL ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler ) +{ + return connectWithCompletion(_rxHandler,false); +} + +Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password) +{ + return getConnection(user,password,false); +} + +Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnection( const OUString& user, const OUString& password ) +{ + return getConnection(user,password,true); +} + +Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnectionWithCompletion( const Reference< XInteractionHandler >& _rxHandler ) +{ + return connectWithCompletion(_rxHandler,true); +} + +Reference< XConnection > ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler,bool _bIsolated ) +{ + ModelMethodGuard aGuard( *this ); + + if (!_rxHandler.is()) + { + SAL_WARN("dbaccess","ODatabaseSource::connectWithCompletion: invalid interaction handler!"); + return getConnection(m_pImpl->m_sUser, m_pImpl->m_aPassword,_bIsolated); + } + + OUString sUser(m_pImpl->m_sUser), sPassword(m_pImpl->m_aPassword); + bool bNewPasswordGiven = false; + + if (m_pImpl->m_bPasswordRequired && sPassword.isEmpty()) + { // we need a password, but don't have one yet. + // -> ask the user + + // build an interaction request + // two continuations (Ok and Cancel) + rtl::Reference pAbort = new OInteractionAbort; + rtl::Reference pAuthenticate = new OAuthenticationContinuation; + + // the name which should be referred in the login dialog + OUString sServerName( m_pImpl->m_sName ); + INetURLObject aURLCheck( sServerName ); + if ( aURLCheck.GetProtocol() != INetProtocol::NotValid ) + sServerName = aURLCheck.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous ); + + // the request + AuthenticationRequest aRequest; + aRequest.ServerName = sServerName; + aRequest.HasRealm = aRequest.HasAccount = false; + aRequest.HasUserName = aRequest.HasPassword = true; + aRequest.UserName = m_pImpl->m_sUser; + aRequest.Password = m_pImpl->m_sFailedPassword.isEmpty() ? m_pImpl->m_aPassword : m_pImpl->m_sFailedPassword; + rtl::Reference pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + pRequest->addContinuation(pAbort); + pRequest->addContinuation(pAuthenticate); + + // handle the request + try + { + _rxHandler->handle(pRequest); + } + catch(Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if (!pAuthenticate->wasSelected()) + return Reference< XConnection >(); + + // get the result + sUser = m_pImpl->m_sUser = pAuthenticate->getUser(); + sPassword = pAuthenticate->getPassword(); + + if (pAuthenticate->getRememberPassword()) + { + m_pImpl->m_aPassword = pAuthenticate->getPassword(); + bNewPasswordGiven = true; + } + m_pImpl->m_sFailedPassword.clear(); + } + + try + { + return getConnection(sUser, sPassword,_bIsolated); + } + catch(Exception&) + { + if (bNewPasswordGiven) + { + m_pImpl->m_sFailedPassword = m_pImpl->m_aPassword; + // assume that we had an authentication problem. Without this we may, after an unsuccessful connect, while + // the user gave us a password and the order to remember it, never allow a password input again (at least + // not without restarting the session) + m_pImpl->m_aPassword.clear(); + } + throw; + } +} + +Reference< XConnection > ODatabaseSource::buildIsolatedConnection(const OUString& user, const OUString& password) +{ + Reference< XConnection > xConn; + Reference< XConnection > xSdbcConn = buildLowLevelConnection(user, password); + OSL_ENSURE( xSdbcConn.is(), "ODatabaseSource::buildIsolatedConnection: invalid return value of buildLowLevelConnection!" ); + // buildLowLevelConnection is expected to always succeed + if ( xSdbcConn.is() ) + { + // build a connection server and return it (no stubs) + xConn = new OConnection(*this, xSdbcConn, m_pImpl->m_aContext); + } + return xConn; +} + +Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password,bool _bIsolated) +{ + ModelMethodGuard aGuard( *this ); + + Reference< XConnection > xConn; + if ( _bIsolated ) + { + xConn = buildIsolatedConnection(user,password); + } + else + { // create a new proxy for the connection + if ( !m_pImpl->m_xSharedConnectionManager.is() ) + { + // TODO ideally we could just have one field, but to make that work + // we'd need to move OSharedConnectionManager into its own file and header + rtl::Reference manager = + new OSharedConnectionManager( m_pImpl->m_aContext ); + m_pImpl->m_pSharedConnectionManager = manager.get(); + m_pImpl->m_xSharedConnectionManager = m_pImpl->m_pSharedConnectionManager; + } + xConn = m_pImpl->m_pSharedConnectionManager->getConnection( + m_pImpl->m_sConnectURL, user, password, m_pImpl->m_xSettings->getPropertyValues(), this ); + } + + if ( xConn.is() ) + { + Reference< XComponent> xComp(xConn,UNO_QUERY); + if ( xComp.is() ) + xComp->addEventListener( static_cast< XContainerListener* >( this ) ); + m_pImpl->m_aConnections.emplace_back(xConn); + } + + return xConn; +} + +Reference< XNameAccess > SAL_CALL ODatabaseSource::getBookmarks( ) +{ + ModelMethodGuard aGuard( *this ); + // tdf#114596 this may look nutty but see OBookmarkContainer::acquire() + return static_cast(&m_Bookmarks); +} + +Reference< XNameAccess > SAL_CALL ODatabaseSource::getQueryDefinitions( ) +{ + ModelMethodGuard aGuard( *this ); + + Reference< XNameAccess > xContainer = m_pImpl->m_xCommandDefinitions; + if ( !xContainer.is() ) + { + Any aValue; + css::uno::Reference< css::uno::XInterface > xMy(*this); + if ( dbtools::getDataSourceSetting(xMy,"CommandDefinitions",aValue) ) + { + OUString sSupportService; + aValue >>= sSupportService; + if ( !sSupportService.isEmpty() ) + { + Sequence aArgs{ Any(NamedValue("DataSource",Any(xMy))) }; + xContainer.set( m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(sSupportService, aArgs, m_pImpl->m_aContext), UNO_QUERY); + } + } + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_QUERY ) ); + xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, false ); + } + m_pImpl->m_xCommandDefinitions = xContainer; + } + return xContainer; +} + +// XTablesSupplier +Reference< XNameAccess > ODatabaseSource::getTables() +{ + ModelMethodGuard aGuard( *this ); + + Reference< XNameAccess > xContainer = m_pImpl->m_xTableDefinitions; + if ( !xContainer.is() ) + { + TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_TABLE ) ); + xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, true ); + m_pImpl->m_xTableDefinitions = xContainer; + } + return xContainer; +} + +void SAL_CALL ODatabaseSource::flush( ) +{ + try + { + // SYNCHRONIZED -> + { + ModelMethodGuard aGuard( *this ); + + typedef ::utl::SharedUNOComponent< XModel, ::utl::CloseableComponent > SharedModel; + SharedModel xModel( m_pImpl->getModel_noCreate(), SharedModel::NoTakeOwnership ); + + if ( !xModel.is() ) + xModel.reset( m_pImpl->createNewModel_deliverOwnership(), SharedModel::TakeOwnership ); + + Reference< css::frame::XStorable> xStorable( xModel, UNO_QUERY_THROW ); + xStorable->store(); + } + // <- SYNCHRONIZED + + css::lang::EventObject aFlushedEvent(*this); + m_aFlushListeners.notifyEach( &XFlushListener::flushed, aFlushedEvent ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL ODatabaseSource::flushed( const EventObject& /*rEvent*/ ) +{ + ModelMethodGuard aGuard( *this ); + + // Okay, this is some hack. + // + // In general, we have the problem that embedded databases write into their underlying storage, which + // logically is one of our sub storage, and practically is a temporary file maintained by the + // package implementation. As long as we did not commit this storage and our main storage, + // the changes made by the embedded database engine are not really reflected in the database document + // file. This is Bad (TM) for a "real" database application - imagine somebody entering some + // data, and then crashing: For a database application, you would expect that the data still is present + // when you connect to the database next time. + // + // Since this is a conceptual problem as long as we do use those ZIP packages (in fact, we *cannot* + // provide the desired functionality as long as we do not have a package format which allows O(1) writes), + // we cannot completely fix this. However, we can relax the problem by committing more often - often + // enough so that data loss is more seldom, and seldom enough so that there's no noticeable performance + // decrease. + // + // For this, we introduced a few places which XFlushable::flush their connections, and register as + // XFlushListener at the embedded connection (which needs to provide the XFlushable functionality). + // Then, when the connection is flushed, we commit both the database storage and our main storage. + // + // #i55274# + + OSL_ENSURE( m_pImpl->isEmbeddedDatabase(), "ODatabaseSource::flushed: no embedded database?!" ); + bool bWasModified = m_pImpl->m_bModified; + m_pImpl->commitEmbeddedStorage(); + m_pImpl->setModified( bWasModified ); +} + +void SAL_CALL ODatabaseSource::addFlushListener( const Reference< css::util::XFlushListener >& _xListener ) +{ + m_aFlushListeners.addInterface(_xListener); +} + +void SAL_CALL ODatabaseSource::removeFlushListener( const Reference< css::util::XFlushListener >& _xListener ) +{ + m_aFlushListeners.removeInterface(_xListener); +} + +void SAL_CALL ODatabaseSource::elementInserted( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +void SAL_CALL ODatabaseSource::elementRemoved( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +void SAL_CALL ODatabaseSource::elementReplaced( const ContainerEvent& /*Event*/ ) +{ + ModelMethodGuard aGuard( *this ); + if ( m_pImpl.is() ) + m_pImpl->setModified(true); +} + +// XDocumentDataSource +Reference< XOfficeDatabaseDocument > SAL_CALL ODatabaseSource::getDatabaseDocument() +{ + ModelMethodGuard aGuard( *this ); + + Reference< XModel > xModel( m_pImpl->getModel_noCreate() ); + if ( !xModel.is() ) + xModel = m_pImpl->createNewModel_deliverOwnership(); + + return Reference< XOfficeDatabaseDocument >( xModel, UNO_QUERY_THROW ); +} + +void SAL_CALL ODatabaseSource::initialize( css::uno::Sequence< css::uno::Any > const & rArguments) +{ + ::comphelper::NamedValueCollection aProperties( rArguments ); + if (aProperties.has("ParentWindow")) + aProperties.get("ParentWindow") >>= m_pImpl->m_xDialogParent; +} + +Reference< XInterface > ODatabaseSource::getThis() const +{ + return *const_cast< ODatabaseSource* >( this ); +} + +} // namespace dbaccess + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_dba_ODatabaseSource(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + css::uno::Reference inst( + DatabaseContext::create(context)->createInstance()); + inst->acquire(); + return inst.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/datasource.hxx b/dbaccess/source/core/dataaccess/datasource.hxx new file mode 100644 index 000000000..5b5985eac --- /dev/null +++ b/dbaccess/source/core/dataaccess/datasource.hxx @@ -0,0 +1,220 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dbaccess +{ + +class OSharedConnectionManager; + +// ODatabaseSource +typedef ::cppu::WeakComponentImplHelper< css::lang::XServiceInfo + , css::sdbc::XDataSource + , css::sdb::XBookmarksSupplier + , css::sdb::XQueryDefinitionsSupplier + , css::sdb::XCompletedConnection + , css::container::XContainerListener + , css::sdbc::XIsolatedConnection + , css::sdbcx::XTablesSupplier + , css::util::XFlushable + , css::util::XFlushListener + , css::sdb::XDocumentDataSource + , css::lang::XInitialization + > ODatabaseSource_Base; + +class ODatabaseSource :public ModelDependentComponent // must be first + ,public ODatabaseSource_Base + ,public ::cppu::OPropertySetHelper + ,public ::comphelper::OPropertyArrayUsageHelper < ODatabaseSource > +{ + friend class ODatabaseContext; + friend class OConnection; + friend class OSharedConnectionManager; + +private: + using ODatabaseSource_Base::rBHelper; + // note: this thing uses the ref-count of "this", see OBookmarkContainer::acquire! + OBookmarkContainer m_Bookmarks; + ::comphelper::OInterfaceContainerHelper3 m_aFlushListeners; + +private: + virtual ~ODatabaseSource() override; + +public: + explicit ODatabaseSource( const ::rtl::Reference< ODatabaseModelImpl >& _pImpl ); + + struct DBContextAccess { friend class ODatabaseContext; private: DBContextAccess() { } }; + + /** sets a new name for the data source + + The name of a data source (our m_sName member) is the registration name, *if* the + data source actually *is* registered at the database context. + + Normally, this name is passed at time of creation of the ODatabaseModelImpl instance, + but if a newly created data source is registered, then it must be possible to propagate + the new registration name. + */ + static void setName( + const css::uno::Reference< css::sdb::XDocumentDataSource >& _rxDocument, + const OUString& _rNewName, + DBContextAccess + ); + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + // css::sdbcx::XTablesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getTables( ) override; + +// css::lang::XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + +// css::uno::XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + +// css::lang::XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +// OComponentHelper + virtual void SAL_CALL disposing() override; + +// css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +// comphelper::OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + +// cppu::OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue + ) override; + virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override; + +// css::sdb::XCompletedConnection + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL connectWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override; + +// css::sdbc::XDataSource + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getConnection( const OUString& user, const OUString& password ) override; + virtual void SAL_CALL setLoginTimeout( sal_Int32 seconds ) override; + virtual sal_Int32 SAL_CALL getLoginTimeout( ) override; + +//::css::sdb::XBookmarksSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getBookmarks( ) override; + +//::css::sdb::XQueryDefinitionsSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getQueryDefinitions( ) override; + +// css::sdbc::XIsolatedConnection + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getIsolatedConnection( const OUString& user, const OUString& password ) override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getIsolatedConnectionWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler ) override; + +// XFlushable + virtual void SAL_CALL flush( ) override; + virtual void SAL_CALL addFlushListener( const css::uno::Reference< css::util::XFlushListener >& l ) override; + virtual void SAL_CALL removeFlushListener( const css::uno::Reference< css::util::XFlushListener >& l ) override; + + // XFlushListener + virtual void SAL_CALL flushed( const css::lang::EventObject& rEvent ) override; + + // XDocumentDataSource + virtual css::uno::Reference< css::sdb::XOfficeDatabaseDocument > SAL_CALL getDatabaseDocument() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +protected: + // ModelDependentComponent overridables + virtual css::uno::Reference< css::uno::XInterface > getThis() const override; + +private: +// helper + /** open a connection for the current settings. this is the simple connection we get from the driver + manager, so it can be used as a master for a "high level" sdb connection. + */ + css::uno::Reference< css::sdbc::XConnection > buildLowLevelConnection( + const OUString& _rUid, const OUString& _rPwd + ); + + css::uno::Reference< css::sdbc::XConnection > buildIsolatedConnection( + const OUString& user, const OUString& password + ); + + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XConnection > getConnection( const OUString& user, const OUString& password , bool _bIsolated); + /// @throws css::sdbc::SQLException + /// @throws css::uno::RuntimeException + css::uno::Reference< css::sdbc::XConnection > connectWithCompletion( const css::uno::Reference< css::task::XInteractionHandler >& handler , bool _bIsolated); + +protected: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/definitioncontainer.cxx b/dbaccess/source/core/dataaccess/definitioncontainer.cxx new file mode 100644 index 000000000..81d8b0302 --- /dev/null +++ b/dbaccess/source/core/dataaccess/definitioncontainer.cxx @@ -0,0 +1,679 @@ +/* -*- 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::com::sun::star::ucb; + +namespace dbaccess +{ + +// ODefinitionContainer_Impl +void ODefinitionContainer_Impl::erase( const TContentPtr& _pDefinition ) +{ + NamedDefinitions::const_iterator aPos = find( _pDefinition ); + if ( aPos != end() ) + m_aDefinitions.erase( aPos ); +} + +ODefinitionContainer_Impl::const_iterator ODefinitionContainer_Impl::find( const TContentPtr& _pDefinition ) const +{ + return std::find_if( + m_aDefinitions.begin(), + m_aDefinitions.end(), + [&_pDefinition] (const NamedDefinitions::value_type& namedDef) { + return namedDef.second == _pDefinition; + }); +} + +ODefinitionContainer_Impl::iterator ODefinitionContainer_Impl::find( const TContentPtr& _pDefinition ) +{ + return std::find_if( + m_aDefinitions.begin(), + m_aDefinitions.end(), + [&_pDefinition] (const NamedDefinitions::value_type& namedDef) { + return namedDef.second == _pDefinition; + }); +} + +// ODefinitionContainer + +ODefinitionContainer::ODefinitionContainer( const Reference< XComponentContext >& _xORB + , const Reference< XInterface >& _xParentContainer + , const TContentPtr& _pImpl + , bool _bCheckSlash + ) + :OContentHelper(_xORB,_xParentContainer,_pImpl) + ,m_aApproveListeners(m_aMutex) + ,m_aContainerListeners(m_aMutex) + ,m_bInPropertyChange(false) + ,m_bCheckSlash(_bCheckSlash) +{ + assert(m_pImpl); + m_pImpl->m_aProps.bIsDocument = false; + m_pImpl->m_aProps.bIsFolder = true; + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + for (auto const& definition : rDefinitions) + m_aDocuments.push_back( + m_aDocumentMap.emplace(definition.first, Documents::mapped_type() ).first ); + +} + +void SAL_CALL ODefinitionContainer::disposing() +{ + OContentHelper::disposing(); + + MutexGuard aGuard(m_aMutex); + + // say goodbye to our listeners + EventObject aEvt(*this); + m_aApproveListeners.disposeAndClear(aEvt); + m_aContainerListeners.disposeAndClear(aEvt); + + // dispose our elements + for (auto const& elem : m_aDocumentMap) + { + Reference xProp = elem.second; + if ( xProp.is() ) + { + removeObjectListener(xProp); + ::comphelper::disposeComponent(xProp); + } + } + + // remove our elements + m_aDocuments.clear(); + // !!! do this before clearing the map which the vector elements refer to !!! + m_aDocumentMap.clear(); +} + +ODefinitionContainer::~ODefinitionContainer() +{ +} + +IMPLEMENT_FORWARD_XINTERFACE2( ODefinitionContainer,OContentHelper,ODefinitionContainer_Base) +css::uno::Sequence< css::uno::Type > ODefinitionContainer::getTypes() +{ + return ::comphelper::concatSequences( + OContentHelper::getTypes( ), + ODefinitionContainer_Base::getTypes( ) + ); +} + + +css::uno::Sequence ODefinitionContainer::getImplementationId() +{ + return css::uno::Sequence(); +} + +// XServiceInfo +OUString SAL_CALL ODefinitionContainer::getImplementationName( ) +{ + return "com.sun.star.sdb.ODefinitionContainer"; +} + +Sequence< OUString > SAL_CALL ODefinitionContainer::getSupportedServiceNames( ) +{ + return { "com.sun.star.sdb.DefinitionContainer", "com.sun.star.ucb.Content" }; +} + +// XNameContainer +void SAL_CALL ODefinitionContainer::insertByName( const OUString& _rName, const Any& aElement ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // approve the new object + Reference< XContent > xNewElement(aElement,UNO_QUERY); + approveNewObject( _rName, xNewElement ); // will throw if necessary + + notifyByName( aGuard, _rName, xNewElement, nullptr, E_INSERTED, ApproveListeners ); + implAppend( _rName, xNewElement ); + notifyByName( aGuard, _rName, xNewElement, nullptr, E_INSERTED, ContainerListemers ); +} + +void SAL_CALL ODefinitionContainer::removeByName( const OUString& _rName ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(_rName,*this); + + // the old element (for the notifications) + Reference< XContent > xOldElement = implGetByName( _rName, impl_haveAnyListeners_nothrow() ); + + // do the removal + notifyByName( aGuard, _rName, nullptr, xOldElement, E_REMOVED, ApproveListeners ); + implRemove( _rName ); + notifyByName( aGuard, _rName, nullptr, xOldElement, E_REMOVED, ContainerListemers ); + + removeObjectListener( xOldElement ); + disposeComponent(xOldElement); +} + +// XNameReplace +void SAL_CALL ODefinitionContainer::replaceByName( const OUString& _rName, const Any& aElement ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + try + { + // let derived classes approve the new object + Reference< XContent > xNewElement(aElement,UNO_QUERY); + approveNewObject( _rName, xNewElement ); // will throw if necessary + + // the old element (for the notifications) + Reference< XContent > xOldElement = implGetByName( _rName, impl_haveAnyListeners_nothrow() ); + + notifyByName( aGuard, _rName, xNewElement, xOldElement, E_REPLACED, ApproveListeners ); + implReplace( _rName, xNewElement ); + notifyByName( aGuard, _rName, xNewElement, xOldElement, E_REPLACED, ContainerListemers ); + + // and dispose it + disposeComponent(xOldElement); + } + catch (const RuntimeException&) + { + throw; + } + catch (const NoSuchElementException&) + { + throw; + } + catch (const WrappedTargetException&) + { + throw; + } + catch (const Exception& e) + { + css::uno::Any a(cppu::getCaughtException()); + throw css::lang::WrappedTargetException( + "wrapped Exception " + e.Message, + css::uno::Reference(), a); + } +} + +namespace +{ + typedef Reference< XVeto > ( SAL_CALL XContainerApproveListener::*ContainerApprovalMethod )( const ContainerEvent& ); + + struct RaiseExceptionFromVeto + { + private: + ContainerApprovalMethod m_pMethod; + const ContainerEvent& m_rEvent; + + public: + explicit RaiseExceptionFromVeto( ContainerApprovalMethod _pMethod, const ContainerEvent& _rEvent ) + :m_pMethod( _pMethod ) + ,m_rEvent( _rEvent ) + { + } + + void operator()( const Reference< XContainerApproveListener >& Listener ) const + { + Reference< XVeto > xVeto = (Listener.get()->*m_pMethod)( m_rEvent ); + if ( !xVeto.is() ) + return; + + Any eVetoDetails = xVeto->getDetails(); + + IllegalArgumentException aIllegalArgumentError; + if ( eVetoDetails >>= aIllegalArgumentError ) + throw aIllegalArgumentError; + + WrappedTargetException aWrappedError; + if ( eVetoDetails >>= aWrappedError ) + throw aWrappedError; + + throw WrappedTargetException( xVeto->getReason(), Listener, eVetoDetails ); + } + }; +} + +void ODefinitionContainer::notifyByName( ResettableMutexGuard& _rGuard, const OUString& _rName, + const Reference< XContent >& _xNewElement, const Reference< XContent >& _xOldElement, + ContainerOperation _eOperation, ListenerType _eType ) +{ + bool bApprove = ( _eType == ApproveListeners ); + + ::comphelper::OInterfaceContainerHelper2& rContainer( bApprove ? m_aApproveListeners : m_aContainerListeners ); + if ( !rContainer.getLength() ) + return; + + ContainerEvent aEvent( *this, Any( _rName ), Any( _xNewElement ), Any( _xOldElement ) ); + + _rGuard.clear(); + switch ( _eOperation ) + { + case E_INSERTED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveInsertElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementInserted, aEvent ); + break; + case E_REPLACED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveReplaceElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementReplaced, aEvent ); + break; + case E_REMOVED: + if ( bApprove ) + rContainer.forEach< XContainerApproveListener, RaiseExceptionFromVeto >( + RaiseExceptionFromVeto( &XContainerApproveListener::approveRemoveElement, aEvent ) ); + else + rContainer.notifyEach( &XContainerListener::elementRemoved, aEvent ); + break; + } + + if ( bApprove ) + _rGuard.reset(); +} + +void SAL_CALL ODefinitionContainer::addContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + if (_rxListener.is()) + m_aContainerListeners.addInterface(_rxListener); +} + +void SAL_CALL ODefinitionContainer::removeContainerListener( const Reference< XContainerListener >& _rxListener ) +{ + if (_rxListener.is()) + m_aContainerListeners.removeInterface(_rxListener); +} + +void SAL_CALL ODefinitionContainer::addContainerApproveListener( const Reference< XContainerApproveListener >& Listener ) +{ + if ( Listener.is() ) + m_aApproveListeners.addInterface( Listener ); +} + +void SAL_CALL ODefinitionContainer::removeContainerApproveListener( const Reference< XContainerApproveListener >& Listener ) +{ + if ( Listener.is() ) + m_aApproveListeners.removeInterface( Listener ); +} + +// XElementAccess +Type SAL_CALL ODefinitionContainer::getElementType( ) +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL ODefinitionContainer::hasElements( ) +{ + MutexGuard aGuard(m_aMutex); + return !m_aDocuments.empty(); +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL ODefinitionContainer::createEnumeration( ) +{ + MutexGuard aGuard(m_aMutex); + return new ::comphelper::OEnumerationByIndex(static_cast(this)); +} + +// XIndexAccess +sal_Int32 SAL_CALL ODefinitionContainer::getCount( ) +{ + MutexGuard aGuard(m_aMutex); + return m_aDocuments.size(); +} + +Any SAL_CALL ODefinitionContainer::getByIndex( sal_Int32 _nIndex ) +{ + MutexGuard aGuard(m_aMutex); + + if ((_nIndex < 0) || (o3tl::make_unsigned(_nIndex) >= m_aDocuments.size())) + throw IndexOutOfBoundsException(); + + Documents::iterator aPos = m_aDocuments[_nIndex]; + Reference xProp = aPos->second; + if (!xProp.is()) + { // that's the first access to the object + // -> create it + xProp = createObject(aPos->first); + aPos->second = Documents::mapped_type(); + // and update the name-access map + } + + return Any(xProp); +} + +Any SAL_CALL ODefinitionContainer::getByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_aMutex); + + return Any( implGetByName( _rName, true ) ); +} + +Reference< XContent > ODefinitionContainer::implGetByName(const OUString& _rName, bool _bReadIfNecessary) +{ + Documents::iterator aMapPos = m_aDocumentMap.find(_rName); + if (aMapPos == m_aDocumentMap.end()) + throw NoSuchElementException(_rName,*this); + + Reference< XContent > xProp = aMapPos->second; + + if (_bReadIfNecessary && !xProp.is()) + { // the object has never been accessed before, so we have to read it now + // (that's the expensive part) + + // create the object and insert it into the map + xProp = createObject(_rName); + aMapPos->second = xProp; + addObjectListener(xProp); + } + + return xProp; +} + +Sequence< OUString > SAL_CALL ODefinitionContainer::getElementNames( ) +{ + MutexGuard aGuard(m_aMutex); + + Sequence< OUString > aNames(m_aDocumentMap.size()); + OUString* pNames = aNames.getArray(); + for (auto const& elem : m_aDocumentMap) + { + *pNames = elem.first; + ++pNames; + } + + return aNames; +} + +sal_Bool SAL_CALL ODefinitionContainer::hasByName( const OUString& _rName ) +{ + MutexGuard aGuard(m_aMutex); + + return checkExistence(_rName); +} + +void SAL_CALL ODefinitionContainer::disposing( const EventObject& _rSource ) +{ + MutexGuard aGuard(m_aMutex); + Reference< XContent > xSource(_rSource.Source, UNO_QUERY); + // it's one of our documents... + for (auto & elem : m_aDocumentMap) + { + if ( xSource == elem.second.get() ) + { + removeObjectListener(xSource); + // and clear our document map/vector, so the object will be recreated on next access + elem.second = Documents::mapped_type(); + } + } +} + +void ODefinitionContainer::implRemove(const OUString& _rName) +{ + // from the object maps + Documents::const_iterator aFind = m_aDocumentMap.find(_rName); + if ( aFind != m_aDocumentMap.end() ) + { + m_aDocuments.erase( std::find(m_aDocuments.begin(),m_aDocuments.end(),aFind)); + m_aDocumentMap.erase(aFind); + + getDefinitions().erase( _rName ); + + notifyDataSourceModified(); + } +} + +namespace +{ + bool lcl_ensureName( const Reference< XContent >& _rxContent, const OUString& _rName ) + { + if ( !_rxContent.is() ) + return true; + + // obtain the current name. If it's the same as the new one, + // don't do anything + try + { + Reference< XPropertySet > xProps( _rxContent, UNO_QUERY ); + if ( xProps.is() ) + { + OUString sCurrentName; + OSL_VERIFY( xProps->getPropertyValue( PROPERTY_NAME ) >>= sCurrentName ); + if ( sCurrentName == _rName ) + return true; + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_ensureName: caught an exception while obtaining the current name!" ); + } + + // set the new name + Reference< XRename > xRename( _rxContent, UNO_QUERY ); + OSL_ENSURE( xRename.is(), "lcl_ensureName: invalid content (not renameable)!" ); + if ( !xRename.is() ) + return false; + try + { + xRename->rename( _rName ); + return true; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "lcl_ensureName" ); + } + return false; + } +} + +void ODefinitionContainer::implAppend(const OUString& _rName, const Reference< XContent >& _rxNewObject) +{ + MutexGuard aGuard(m_aMutex); + try + { + Reference xChild(_rxNewObject,UNO_QUERY); + if ( xChild.is() ) + xChild->setParent(static_cast(this)); + + ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( _rName ); + if ( aFind == rDefinitions.end() ) + { + // ensure that the new object has the proper name. + // Somebody could create an object with name "foo", and insert it as "bar" + // into a container. In this case, we need to ensure that the object name + // is also "bar" + // #i44786# + lcl_ensureName( _rxNewObject, _rName ); + + ::rtl::Reference< OContentHelper > pContent = comphelper::getFromUnoTunnel( _rxNewObject ); + if ( pContent.is() ) + { + TContentPtr pImpl = pContent->getImpl(); + rDefinitions.erase( pImpl ); + pImpl->m_aProps.aTitle = _rName; + rDefinitions.insert( _rName, pImpl ); + } + } + + m_aDocuments.push_back(m_aDocumentMap.emplace(_rName,_rxNewObject).first); + notifyDataSourceModified(); + // now update our structures + if ( _rxNewObject.is() ) + addObjectListener(_rxNewObject); + } + catch(Exception&) + { + OSL_FAIL("ODefinitionContainer::implAppend: caught something !"); + } +} + +void ODefinitionContainer::implReplace(const OUString& _rName, const Reference< XContent >& _rxNewObject) +{ + OSL_ENSURE(checkExistence(_rName), "ODefinitionContainer::implReplace : invalid name !"); + + Documents::iterator aFind = m_aDocumentMap.find(_rName); + removeObjectListener(aFind->second); + aFind->second = _rxNewObject; + addObjectListener(aFind->second); +} + +void ODefinitionContainer::approveNewObject(const OUString& _sName,const Reference< XContent >& _rxObject) const +{ + // check the arguments + if ( _sName.isEmpty() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_NAME_MUST_NOT_BE_EMPTY ), + *this, + 0 ); + + if ( m_bCheckSlash && _sName.indexOf( '/' ) != -1 ) + throw IllegalArgumentException( + m_aErrorHelper.getErrorMessage( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES ), + *this, + 0 ); + + if ( !_rxObject.is() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_NO_NULL_OBJECTS_IN_CONTAINER ), + *this, + 0 ); + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + if ( rDefinitions.find( _sName ) != rDefinitions.end() ) + throw ElementExistException( + DBA_RES( RID_STR_NAME_ALREADY_USED ), + *this ); + + ::rtl::Reference< OContentHelper > pContent( comphelper::getFromUnoTunnel( _rxObject ) ); + if ( !pContent.is() ) + throw IllegalArgumentException( + DBA_RES( RID_STR_OBJECT_CONTAINER_MISMATCH ), + *this, + 1 ); + + if ( rDefinitions.find( pContent->getImpl() ) != rDefinitions.end() ) + throw ElementExistException( + DBA_RES( RID_STR_OBJECT_ALREADY_CONTAINED ), + *this ); +} + +// XPropertyChangeListener +void SAL_CALL ODefinitionContainer::propertyChange( const PropertyChangeEvent& evt ) +{ + if( evt.PropertyName != PROPERTY_NAME && evt.PropertyName != "Title" ) + return; + + MutexGuard aGuard(m_aMutex); + + m_bInPropertyChange = true; + try + { + OUString sNewName,sOldName; + evt.OldValue >>= sOldName; + evt.NewValue >>= sNewName; + Reference xContent( evt.Source, UNO_QUERY ); + removeObjectListener( xContent ); + implRemove( sOldName ); + implAppend( sNewName, xContent ); + } + catch(const Exception& ex) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( ex.Message, + nullptr, anyEx ); + } + m_bInPropertyChange = false; +} + +// XVetoableChangeListener +void SAL_CALL ODefinitionContainer::vetoableChange( const PropertyChangeEvent& aEvent ) +{ + MutexGuard aGuard(m_aMutex); + + if( aEvent.PropertyName == PROPERTY_NAME || aEvent.PropertyName == "Title" ) + { + OUString sNewName; + aEvent.NewValue >>= sNewName; + if(hasByName(sNewName)) + throw PropertyVetoException(); + } +} + +void ODefinitionContainer::addObjectListener(const Reference< XContent >& _xNewObject) +{ + OSL_ENSURE(_xNewObject.is(),"ODefinitionContainer::addObjectListener: Object is null!"); + Reference xProp(_xNewObject,UNO_QUERY); + if ( xProp.is() ) + { + xProp->addPropertyChangeListener(PROPERTY_NAME, this); + xProp->addVetoableChangeListener(PROPERTY_NAME, this); + } +} + +void ODefinitionContainer::removeObjectListener(const Reference< XContent >& _xNewObject) +{ + Reference xProp(_xNewObject,UNO_QUERY); + if ( xProp.is() ) + { + xProp->removePropertyChangeListener(PROPERTY_NAME, this); + xProp->removeVetoableChangeListener(PROPERTY_NAME, this); + } +} + +bool ODefinitionContainer::checkExistence(const OUString& _rName) +{ + return m_aDocumentMap.find(_rName) != m_aDocumentMap.end(); +} + +} + +// namespace dbaccess +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentcontainer.cxx b/dbaccess/source/core/dataaccess/documentcontainer.cxx new file mode 100644 index 000000000..6a9f17adb --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentcontainer.cxx @@ -0,0 +1,770 @@ +/* -*- 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 "documentcontainer.hxx" +#include +#include "documentdefinition.hxx" +#include +#include +#include +#include +#include "myucp_resultset.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::io; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +namespace dbaccess +{ + +namespace { + +// LocalNameApproval +class LocalNameApproval : public IContainerApprove +{ + ::connectivity::SQLError m_aErrors; + +public: + void approveElement( const OUString& _rName ) override; +}; + +} + +void LocalNameApproval::approveElement( const OUString& _rName ) +{ + if ( _rName.indexOf( '/' ) != -1 ) + throw IllegalArgumentException( + m_aErrors.getErrorMessage( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES ), + nullptr, + 0 + ); +} + +// ODocumentContainer + +ODocumentContainer::ODocumentContainer(const Reference< XComponentContext >& _xORB + ,const Reference< XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + , bool _bFormsContainer + ) + :ODefinitionContainer(_xORB,_xParentContainer,_pImpl) + ,OPropertyStateContainer(OContentHelper::rBHelper) + ,m_bFormsContainer(_bFormsContainer) +{ + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::BOUND | PropertyAttribute::READONLY | PropertyAttribute::CONSTRAINED, + &m_pImpl->m_aProps.aTitle, cppu::UnoTypem_aProps.aTitle)>::get()); + + setElementApproval( std::make_shared() ); +} + +ODocumentContainer::~ODocumentContainer() +{ + + if ( !OContentHelper::rBHelper.bInDispose && !OContentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } +} + +IMPLEMENT_FORWARD_XINTERFACE3( ODocumentContainer,ODefinitionContainer,ODocumentContainer_Base,OPropertyStateContainer) + +css::uno::Sequence ODocumentContainer::getImplementationId() +{ + return css::uno::Sequence(); +} + +css::uno::Sequence< css::uno::Type > ODocumentContainer::getTypes() +{ + return ::comphelper::concatSequences( + ODefinitionContainer::getTypes( ), + OPropertyStateContainer::getTypes( ), + ODocumentContainer_Base::getTypes( ) + ); +} +OUString SAL_CALL ODocumentContainer::getImplementationName() + { + return "com.sun.star.comp.dba.ODocumentContainer"; + }; +sal_Bool SAL_CALL ODocumentContainer::supportsService(const OUString& _rServiceName) + { + const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames()); + for (const OUString& s : aSupported) + if (s == _rServiceName) + return true; + + return false; + }; +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL ODocumentContainer::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} +::cppu::IPropertyArrayHelper& ODocumentContainer::getInfoHelper() +{ + return *ODocumentContainer::getArrayHelper(); +} +::cppu::IPropertyArrayHelper* ODocumentContainer::createArrayHelper( ) const +{ + css::uno::Sequence< css::beans::Property > aProps; + describeProperties(aProps); + return new ::cppu::OPropertyArrayHelper(aProps); +} + + +Sequence< OUString > SAL_CALL ODocumentContainer::getSupportedServiceNames( ) +{ + return { m_bFormsContainer ? OUString(SERVICE_NAME_FORM_COLLECTION) : OUString(SERVICE_NAME_REPORT_COLLECTION) }; +} + +OUString ODocumentContainer::determineContentType() const +{ + return OUString(); +} + +Reference< XContent > ODocumentContainer::createObject( const OUString& _rName) +{ + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( _rName ); + OSL_ENSURE( aFind != rDefinitions.end(), "ODocumentContainer::createObject:Invalid entry in map!" ); + if ( aFind->second->m_aProps.bIsFolder ) + return new ODocumentContainer( m_aContext, *this, aFind->second, m_bFormsContainer ); + return new ODocumentDefinition( *this, m_aContext, aFind->second, m_bFormsContainer ); +} + +Reference< XInterface > SAL_CALL ODocumentContainer::createInstance( const OUString& aServiceSpecifier ) +{ + return createInstanceWithArguments( aServiceSpecifier, Sequence< Any >() ); +} + +namespace +{ + template< class TYPE > + void lcl_extractAndRemove( ::comphelper::NamedValueCollection& io_rArguments, const OUString& i_rName, TYPE& o_rValue ) + { + if ( io_rArguments.has( i_rName ) ) + { + io_rArguments.get_ensureType( i_rName, o_rValue ); + io_rArguments.remove( i_rName ); + } + } +} + +Reference< XInterface > SAL_CALL ODocumentContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& _aArguments ) +{ + Reference< XInterface > xRet; + Reference< XContent > xContent; + if ( ServiceSpecifier == SERVICE_SDB_DOCUMENTDEFINITION ) + { + MutexGuard aGuard(m_aMutex); + + // extract known arguments + OUString sName, sPersistentName, sURL, sMediaType, sDocServiceName; + Reference< XCommandProcessor > xCopyFrom; + Reference< XConnection > xConnection; + bool bAsTemplate( false ); + Sequence< sal_Int8 > aClassID; + + ::comphelper::NamedValueCollection aArgs( _aArguments ); + lcl_extractAndRemove( aArgs, PROPERTY_NAME, sName ); + lcl_extractAndRemove( aArgs, PROPERTY_PERSISTENT_NAME, sPersistentName ); + lcl_extractAndRemove( aArgs, PROPERTY_URL, sURL ); + lcl_extractAndRemove( aArgs, PROPERTY_EMBEDDEDOBJECT, xCopyFrom ); + lcl_extractAndRemove( aArgs, PROPERTY_ACTIVE_CONNECTION, xConnection ); + lcl_extractAndRemove( aArgs, PROPERTY_AS_TEMPLATE, bAsTemplate ); + lcl_extractAndRemove( aArgs, INFO_MEDIATYPE, sMediaType ); + lcl_extractAndRemove( aArgs, "DocumentServiceName" , sDocServiceName ); + + // ClassID has two allowed types, so a special treatment here + Any aClassIDArg = aArgs.get( "ClassID" ); + if ( aClassIDArg.hasValue() ) + { + if ( !( aClassIDArg >>= aClassID ) ) + { + // Extended for usage also with a string + OUString sClassIDString; + if ( !( aClassIDArg >>= sClassIDString ) ) + throw IllegalArgumentException( OUString(), *this, 2 ); + + aClassID = ::comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation( sClassIDString ); + } + +#if OSL_DEBUG_LEVEL > 0 + OUString sClassIDString = ::comphelper::MimeConfigurationHelper::GetStringClassIDRepresentation( aClassID ); + (void)sClassIDString; +#endif + aArgs.remove( "ClassID" ); + } + // Everything which now is still present in the arguments is passed to the embedded object + const Sequence< PropertyValue > aCreationArgs( aArgs.getPropertyValues() ); + + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + bool bNew = sPersistentName.isEmpty(); + if ( bNew ) + { + sPersistentName = "Obj" + OUString::number(rDefinitions.size() + 1); + Reference xElements = getContainerStorage(); + if ( xElements.is() ) + sPersistentName = ::dbtools::createUniqueName(xElements,sPersistentName); + + const bool bNeedClassID = !aClassID.hasElements() && sURL.isEmpty() ; + if ( xCopyFrom.is() ) + { + Sequence aIni{ Any(getContainerStorage()), Any(sPersistentName) }; + Command aCommand; + aCommand.Name = "copyTo"; + aCommand.Argument <<= aIni; + + xCopyFrom->execute(aCommand,-1,Reference< XCommandEnvironment >()); + Reference xProp(xCopyFrom,UNO_QUERY); + if ( xProp.is() && xProp->getPropertySetInfo().is() && xProp->getPropertySetInfo()->hasPropertyByName(PROPERTY_AS_TEMPLATE) ) + xProp->getPropertyValue(PROPERTY_AS_TEMPLATE) >>= bAsTemplate; + + // if we do not have an own class ID, see if we can determine one from the copy we just created + if ( bNeedClassID ) + ODocumentDefinition::GetDocumentServiceFromMediaType( getContainerStorage(), sPersistentName, m_aContext, aClassID ); + } + else + { + if ( bNeedClassID ) + { + if ( !sMediaType.isEmpty() ) + ODocumentDefinition::GetDocumentServiceFromMediaType( sMediaType, m_aContext, aClassID ); + else if ( !sDocServiceName.isEmpty() ) + { + ::comphelper::MimeConfigurationHelper aConfigHelper( m_aContext ); + const Sequence< NamedValue > aProps( aConfigHelper.GetObjectPropsByDocumentName( sDocServiceName ) ); + const ::comphelper::NamedValueCollection aMediaTypeProps( aProps ); + aClassID = aMediaTypeProps.getOrDefault( "ClassID", Sequence< sal_Int8 >() ); + } + } + } + } + + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( sName ); + TContentPtr pElementImpl; + if ( bNew || ( aFind == rDefinitions.end() ) ) + { + pElementImpl = std::make_shared(); + if ( !bNew ) + pElementImpl->m_aProps.aTitle = sName; + + pElementImpl->m_aProps.sPersistentName = sPersistentName; + pElementImpl->m_aProps.bAsTemplate = bAsTemplate; + pElementImpl->m_pDataSource = m_pImpl->m_pDataSource; + } + else + pElementImpl = aFind->second; + + ::rtl::Reference< ODocumentDefinition > pDocDef = new ODocumentDefinition( *this, m_aContext, pElementImpl, m_bFormsContainer ); + if ( aClassID.hasElements() ) + { + pDocDef->initialLoad( aClassID, aCreationArgs, xConnection ); + } + else + { + OSL_ENSURE( !aCreationArgs.hasElements(), "ODocumentContainer::createInstance: additional creation args are lost, if you do not provide a class ID." ); + } + xContent = pDocDef.get(); + + if ( !sURL.isEmpty() ) + { + Sequence aIni{ Any(sURL) }; + Command aCommand; + aCommand.Name = "insert"; + aCommand.Argument <<= aIni; + Reference< XCommandProcessor > xCommandProcessor(xContent,UNO_QUERY); + if ( xContent.is() ) + { + xCommandProcessor->execute(aCommand,-1,Reference< XCommandEnvironment >()); + } + } + } + else if ( ServiceSpecifier == SERVICE_NAME_FORM_COLLECTION || SERVICE_NAME_REPORT_COLLECTION == ServiceSpecifier ) + { + const Any* pBegin = _aArguments.getConstArray(); + const Any* pEnd = pBegin + _aArguments.getLength(); + PropertyValue aValue; + OUString sName; + Reference xCopyFrom; + for(;pBegin != pEnd;++pBegin) + { + *pBegin >>= aValue; + if ( aValue.Name == PROPERTY_NAME) + { + aValue.Value >>= sName; + } + else if ( aValue.Name == PROPERTY_EMBEDDEDOBJECT) + { + xCopyFrom.set(aValue.Value,UNO_QUERY); + } + } + OSL_ENSURE(!sName.isEmpty(),"Invalid name for a document container!"); + const ODefinitionContainer_Impl& rDefinitions( getDefinitions() ); + ODefinitionContainer_Impl::const_iterator aFind = rDefinitions.find( sName ); + TContentPtr pElementImpl; + if ( aFind == rDefinitions.end() ) + { + pElementImpl = std::make_shared(); + pElementImpl->m_aProps.aTitle = sName; + pElementImpl->m_pDataSource = m_pImpl->m_pDataSource; + } + else + pElementImpl = aFind->second; + OSL_ENSURE( pElementImpl ," Invalid entry in map!"); + xContent = new ODocumentContainer( m_aContext, *this, pElementImpl, ServiceSpecifier == SERVICE_NAME_FORM_COLLECTION ); + + // copy children + if ( xCopyFrom.is() ) + { + Sequence< OUString> aSeq = xCopyFrom->getElementNames(); + const OUString* elements = aSeq.getConstArray(); + const OUString* elementsEnd = elements + aSeq.getLength(); + Reference xObjectToCopy; + + Reference xORB(xContent,UNO_QUERY); + OSL_ENSURE(xORB.is(),"No service factory given"); + if ( xORB.is() ) + { + for(;elements != elementsEnd;++elements) + { + xCopyFrom->getByName(*elements) >>= xObjectToCopy; + Sequence aArguments(comphelper::InitAnyPropertySequence( + { + {"Name", Any(*elements)}, // set as folder + {"Parent", Any(xContent)}, + {PROPERTY_EMBEDDEDOBJECT, Any(xObjectToCopy)}, + })); + + OUString sServiceName; + if ( Reference< XNameAccess >( xObjectToCopy, UNO_QUERY ).is() ) + { + if ( m_bFormsContainer ) + sServiceName = SERVICE_NAME_FORM_COLLECTION; + else + sServiceName = SERVICE_NAME_REPORT_COLLECTION; + } + else + sServiceName = SERVICE_SDB_DOCUMENTDEFINITION; + + Reference xNew(xORB->createInstanceWithArguments(sServiceName,aArguments),UNO_QUERY); + Reference xNameContainer(xContent,UNO_QUERY); + if ( xNameContainer.is() ) + xNameContainer->insertByName(*elements,Any(xNew)); + } + } + } + } + xRet = xContent; + return xRet; +} + +Sequence< OUString > SAL_CALL ODocumentContainer::getAvailableServiceNames( ) +{ + return + { + SERVICE_SDB_DOCUMENTDEFINITION, + SERVICE_NAME_FORM_COLLECTION, + SERVICE_NAME_REPORT_COLLECTION + }; +} + +Any SAL_CALL ODocumentContainer::execute( const Command& aCommand, sal_Int32 CommandId, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + if ( aCommand.Name == "open" ) + { + // open command for a folder content + OpenCommandArgument2 aOpenCommand; + if ( !( aCommand.Argument >>= aOpenCommand ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + bool bOpenFolder = + ( ( aOpenCommand.Mode == OpenMode::ALL ) || + ( aOpenCommand.Mode == OpenMode::FOLDERS ) || + ( aOpenCommand.Mode == OpenMode::DOCUMENTS ) ); + + if ( bOpenFolder ) + { + // open as folder - return result set + + Reference< XDynamicResultSet > xSet + = new DynamicResultSet( m_aContext, + this, + aOpenCommand, + Environment ); + aRet <<= xSet; + } + else + { + // Unsupported. + ucbhelper::cancelCommandExecution( + Any( UnsupportedOpenModeException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + sal_Int16( aOpenCommand.Mode ) ) ), + Environment ); + // Unreachable + } + } + else if ( aCommand.Name == "insert" ) + { + // insert + + InsertCommandArgument arg; + if ( !( aCommand.Argument >>= arg ) ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + } + else if ( aCommand.Name == "delete" ) + { + // delete + Sequence< OUString> aSeq = getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + removeByName(*pIter); + + dispose(); + } + else + aRet = OContentHelper::execute(aCommand,CommandId,Environment); + return aRet; +} + +namespace +{ + bool lcl_queryContent(std::u16string_view _sName,Reference< XNameContainer >& _xNameContainer,Any& _rRet,OUString& _sSimpleName) + { + sal_Int32 nIndex = 0; + OUString sName( o3tl::getToken(_sName,0,'/',nIndex) ); + bool bRet = _xNameContainer->hasByName(sName); + if ( bRet ) + { + _sSimpleName = sName; + _rRet = _xNameContainer->getByName(_sSimpleName); + while ( nIndex != -1 && bRet ) + { + sName = o3tl::getToken(_sName,0,'/',nIndex); + _xNameContainer.set(_rRet,UNO_QUERY); + bRet = _xNameContainer.is(); + if ( bRet ) + { + bRet = _xNameContainer->hasByName(sName); + _sSimpleName = sName; + if ( bRet ) + _rRet = _xNameContainer->getByName(sName); + } + } + } + if ( nIndex == -1 ) + _sSimpleName = sName; // a content + else + _xNameContainer.clear(); // a sub folder doesn't exist + return bRet; + } +} + +Reference< XComponent > SAL_CALL ODocumentContainer::loadComponentFromURL( const OUString& _sURL + , const OUString& /*TargetFrameName*/ + , sal_Int32 /*SearchFlags*/ + , const Sequence< PropertyValue >& Arguments ) +{ + ::SolarMutexGuard aSolarGuard; + + MutexGuard aGuard(m_aMutex); + Reference< XComponent > xComp; + try + { + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( !lcl_queryContent(_sURL,xNameContainer,aContent,sName) ) + { + OUString sMessage( + DBA_RES(RID_STR_NAME_NOT_FOUND).replaceFirst("$name$", _sURL)); + throw IllegalArgumentException( sMessage, *this, 1 ); + } + + Reference< XCommandProcessor > xContent(aContent,UNO_QUERY); + if ( xContent.is() ) + { + Command aCommand; + + ::comphelper::NamedValueCollection aArgs( Arguments ); + aCommand.Name = aArgs.getOrDefault( "OpenMode", OUString("open") ); + aArgs.remove( "OpenMode" ); + + OpenCommandArgument2 aOpenCommand; + aOpenCommand.Mode = OpenMode::DOCUMENT; + aArgs.put( "OpenCommandArgument", aOpenCommand ); + + aCommand.Argument <<= aArgs.getPropertyValues(); + xComp.set(xContent->execute(aCommand,xContent->createCommandIdentifier(),Reference< XCommandEnvironment >()),UNO_QUERY); + } + } + catch(const NoSuchElementException&) + { + throw IllegalArgumentException(); + } + catch(const WrappedTargetException &e) + { + throw WrappedTargetRuntimeException(e.Message, e.Context, e.TargetException); + } + return xComp; +} + +Any SAL_CALL ODocumentContainer::getByHierarchicalName( const OUString& _sName ) +{ + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + return aContent; + throw NoSuchElementException(_sName,*this); +} + +sal_Bool SAL_CALL ODocumentContainer::hasByHierarchicalName( const OUString& _sName ) +{ + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + return lcl_queryContent(_sName,xNameContainer,aContent,sName); +} + +// XHierarchicalNameContainer +void SAL_CALL ODocumentContainer::insertByHierarchicalName( const OUString& _sName, const Any& _aElement ) +{ + Reference< XContent > xContent(_aElement,UNO_QUERY); + if ( !xContent.is() ) + throw IllegalArgumentException(); + + MutexGuard aGuard(m_aMutex); + Any aContent; + Reference< XNameContainer > xNameContainer(this); + OUString sName; + if ( lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw ElementExistException(_sName,*this); + + if ( !xNameContainer.is() ) + { + sal_Int32 index = sName.getLength(); + OUString sMessage( + DBA_RES(RID_STR_NO_SUB_FOLDER).replaceFirst("$folder$", + o3tl::getToken(_sName, 0,'/',index))); + throw IllegalArgumentException( sMessage, *this, 1 ); + } + + xNameContainer->insertByName(sName,_aElement); +} + +void SAL_CALL ODocumentContainer::removeByHierarchicalName( const OUString& _sName ) +{ + if ( _sName.isEmpty() ) + throw NoSuchElementException(_sName,*this); + + MutexGuard aGuard(m_aMutex); + Any aContent; + OUString sName; + Reference< XNameContainer > xNameContainer(this); + if ( !lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw NoSuchElementException(_sName,*this); + + xNameContainer->removeByName(sName); +} + +// XHierarchicalNameReplace +void SAL_CALL ODocumentContainer::replaceByHierarchicalName( const OUString& _sName, const Any& _aElement ) +{ + Reference< XContent > xContent(_aElement,UNO_QUERY); + if ( !xContent.is() ) + throw IllegalArgumentException(); + + MutexGuard aGuard(m_aMutex); + Any aContent; + OUString sName; + Reference< XNameContainer > xNameContainer(this); + if ( !lcl_queryContent(_sName,xNameContainer,aContent,sName) ) + throw NoSuchElementException(_sName,*this); + + xNameContainer->replaceByName(sName,_aElement); +} + +OUString SAL_CALL ODocumentContainer::getHierarchicalName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getHierarchicalName( false ); +} + +OUString SAL_CALL ODocumentContainer::composeHierarchicalName( const OUString& i_rRelativeName ) +{ + OUString aBuffer = getHierarchicalName() + "/" + i_rRelativeName; + return aBuffer; +} + +::rtl::Reference ODocumentContainer::getContent(const OUString& _sName) const +{ + ::rtl::Reference pContent; + try + { + pContent = comphelper::getFromUnoTunnel(const_cast(this)->implGetByName( _sName, true )); + } + catch(const Exception&) + { + } + return pContent; +} + +void ODocumentContainer::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const +{ + _rDefault.clear(); +} + +void SAL_CALL ODocumentContainer::commit( ) +{ + MutexGuard aGuard(m_aMutex); + for (auto const& elem : m_aDocumentMap) + { + Reference xTrans(elem.second.get(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->commit(); + } + Reference xTrans(getContainerStorage(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->commit(); +} + +void SAL_CALL ODocumentContainer::revert( ) +{ + MutexGuard aGuard(m_aMutex); + for (auto const& elem : m_aDocumentMap) + { + Reference xTrans(elem.second.get(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->revert(); + } + Reference xTrans(getContainerStorage(),UNO_QUERY); + if ( xTrans.is() ) + xTrans->revert(); +} + +Reference< XStorage> ODocumentContainer::getContainerStorage() const +{ + return m_pImpl->m_pDataSource + ? m_pImpl->m_pDataSource->getStorage( m_bFormsContainer ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + : Reference< XStorage>(); +} + +void SAL_CALL ODocumentContainer::removeByName( const OUString& _rName ) +{ + ResettableMutexGuard aGuard(m_aMutex); + + // check the arguments + if (_rName.isEmpty()) + throw IllegalArgumentException(); + + if (!checkExistence(_rName)) + throw NoSuchElementException(_rName,*this); + + Reference< XCommandProcessor > xContent( implGetByName( _rName, true ), UNO_QUERY ); + if ( xContent.is() ) + { + Command aCommand; + + aCommand.Name = "delete"; + xContent->execute(aCommand,xContent->createCommandIdentifier(),Reference< XCommandEnvironment >()); + } + + // do the removal + implRemove(_rName); + + notifyByName( aGuard, _rName, nullptr, nullptr, E_REMOVED, ContainerListemers ); +} + +void SAL_CALL ODocumentContainer::rename( const OUString& newName ) +{ + try + { + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + if ( newName == m_pImpl->m_aProps.aTitle ) + return; + + sal_Int32 nHandle = PROPERTY_ID_NAME; + Any aOld(m_pImpl->m_aProps.aTitle); + Any aNew(newName); + + aGuard.clear(); + fire(&nHandle, &aNew, &aOld, 1, true ); + m_pImpl->m_aProps.aTitle = newName; + fire(&nHandle, &aNew, &aOld, 1, false ); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(newName,*this); + } +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentcontainer.hxx b/dbaccess/source/core/dataaccess/documentcontainer.hxx new file mode 100644 index 000000000..d47709e80 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentcontainer.hxx @@ -0,0 +1,137 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dbaccess +{ +typedef ::cppu::ImplHelper5 < css::frame::XComponentLoader + , css::lang::XMultiServiceFactory + , css::container::XHierarchicalNameContainer + , css::container::XHierarchicalName + , css::embed::XTransactedObject + > ODocumentContainer_Base; +// ODocumentContainer - collections of database documents (reports/forms) +class ODocumentContainer : public ODefinitionContainer + , public ODocumentContainer_Base + , public ::comphelper::OPropertyStateContainer + , public ::comphelper::OPropertyArrayUsageHelper< ODocumentContainer > +{ + bool m_bFormsContainer; + +public: + /** constructs the container.
+ */ + ODocumentContainer( + const css::uno::Reference< css::uno::XComponentContext >& _xORB + , const css::uno::Reference< css::uno::XInterface >& _xParentContainer + ,const TContentPtr& _pImpl + , bool _bFormsContainer + ); + + // css::uno::XInterface + DECLARE_XINTERFACE( ) + + virtual css::uno::Sequence SAL_CALL getTypes() override; + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + + // css::lang::XServiceInfo + DECLARE_SERVICE_INFO(); + + // XComponentLoader + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentFromURL( const OUString& URL, const OUString& TargetFrameName, sal_Int32 SearchFlags, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + // css::lang::XMultiServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const OUString& ServiceSpecifier, const css::uno::Sequence< css::uno::Any >& Arguments ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override ; + + // XHierarchicalNameAccess + virtual css::uno::Any SAL_CALL getByHierarchicalName( const OUString& _sName ) override; + virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString& _sName ) override; + + // XHierarchicalNameContainer + virtual void SAL_CALL insertByHierarchicalName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByHierarchicalName( const OUString& Name ) override; + + // XHierarchicalName + virtual OUString SAL_CALL getHierarchicalName( ) override; + virtual OUString SAL_CALL composeHierarchicalName( const OUString& aRelativeName ) override; + + // XNameContainer + virtual void SAL_CALL removeByName( const OUString& _rName ) override; + + // XHierarchicalNameReplace + virtual void SAL_CALL replaceByHierarchicalName( const OUString& aName, const css::uno::Any& aElement ) override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XTransactedObject + virtual void SAL_CALL commit( ) override; + virtual void SAL_CALL revert( ) override; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // helper + ::rtl::Reference getContent(const OUString& _sName) const; + css::uno::Reference< css::embed::XStorage > getContainerStorage() const; + +protected: + virtual ~ODocumentContainer() override; + + /** OContentHelper + */ + virtual OUString determineContentType() const override; + + // ODefinitionContainer + virtual css::uno::Reference< css::ucb::XContent > createObject( + const OUString& _rName + ) override; + + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentdefinition.cxx b/dbaccess/source/core/dataaccess/documentdefinition.cxx new file mode 100644 index 000000000..98e84d6d4 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentdefinition.cxx @@ -0,0 +1,2102 @@ +/* -*- 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 "documentdefinition.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "intercept.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace view; +using namespace uno; +using namespace util; +using namespace ucb; +using namespace beans; +using namespace lang; +using namespace awt; +using namespace embed; +using namespace frame; +using namespace document; +using namespace sdbc; +using namespace sdb; +using namespace io; +using namespace container; +using namespace datatransfer; +using namespace task; +using namespace form; +using namespace drawing; +using namespace ::osl; +using namespace ::comphelper; +using namespace ::cppu; + +using sdb::application::XDatabaseDocumentUI; +namespace DatabaseObject = sdb::application::DatabaseObject; + +#define DEFAULT_WIDTH 10000 +#define DEFAULT_HEIGHT 7500 + +namespace dbaccess +{ + + typedef ::std::optional< bool > optional_bool; + + // helper + namespace + { + OUString lcl_determineContentType_nothrow( const Reference< XStorage >& _rxContainerStorage, + const OUString& _rEntityName ) + { + OUString sContentType; + try + { + ::utl::SharedUNOComponent< XPropertySet > xStorageProps( + _rxContainerStorage->openStorageElement( _rEntityName, ElementModes::READ ), UNO_QUERY_THROW ); + OSL_VERIFY( xStorageProps->getPropertyValue( INFO_MEDIATYPE ) >>= sContentType ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sContentType; + } + } + + // OEmbedObjectHolder + typedef ::cppu::WeakComponentImplHelper< embed::XStateChangeListener > TEmbedObjectHolder; + + namespace { + + class OEmbedObjectHolder : public ::cppu::BaseMutex + ,public TEmbedObjectHolder + { + Reference< XEmbeddedObject > m_xBroadCaster; + ODocumentDefinition* m_pDefinition; + bool m_bInStateChange; + protected: + virtual void SAL_CALL disposing() override; + public: + OEmbedObjectHolder(const Reference< XEmbeddedObject >& _xBroadCaster,ODocumentDefinition* _pDefinition) + : TEmbedObjectHolder(m_aMutex) + ,m_xBroadCaster(_xBroadCaster) + ,m_pDefinition(_pDefinition) + ,m_bInStateChange(false) + { + osl_atomic_increment( &m_refCount ); + { + if ( m_xBroadCaster.is() ) + m_xBroadCaster->addStateChangeListener(this); + } + osl_atomic_decrement( &m_refCount ); + } + + virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL disposing( const lang::EventObject& Source ) override; + }; + + } + + void SAL_CALL OEmbedObjectHolder::disposing() + { + if ( m_xBroadCaster.is() ) + m_xBroadCaster->removeStateChangeListener(this); + m_xBroadCaster = nullptr; + m_pDefinition = nullptr; + } + + void SAL_CALL OEmbedObjectHolder::changingState( const lang::EventObject& /*aEvent*/, ::sal_Int32 /*nOldState*/, ::sal_Int32 /*nNewState*/ ) + { + } + + void SAL_CALL OEmbedObjectHolder::stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) + { + if ( !m_bInStateChange && nNewState == EmbedStates::RUNNING && nOldState == EmbedStates::ACTIVE && m_pDefinition ) + { + m_bInStateChange = true; + Reference xHoldAlive(static_cast< ::cppu::OWeakObject* >(m_pDefinition),UNO_QUERY); + { + Reference xEmbeddedObject(aEvent.Source,UNO_QUERY); + if ( xEmbeddedObject.is() ) + xEmbeddedObject->changeState(EmbedStates::LOADED); + } + m_bInStateChange = false; + } + } + + void SAL_CALL OEmbedObjectHolder::disposing( const lang::EventObject& /*Source*/ ) + { + m_xBroadCaster = nullptr; + } + + // OEmbeddedClientHelper + class OEmbeddedClientHelper : public ::cppu::WeakImplHelper + { + public: + virtual void SAL_CALL saveObject( ) override + { + } + // XComponentSupplier + virtual Reference< util::XCloseable > SAL_CALL getComponent( ) override + { + return Reference< css::util::XCloseable >(); + } + + // XEmbeddedClient + virtual void SAL_CALL visibilityChanged( sal_Bool /*bVisible*/ ) override + { + } + }; + + namespace { + + // LockModifiable + class LockModifiable + { + public: + explicit LockModifiable( const Reference< XInterface >& i_rModifiable ) + :m_xModifiable( i_rModifiable, UNO_QUERY ) + { + OSL_ENSURE( m_xModifiable.is(), "LockModifiable::LockModifiable: invalid component!" ); + if ( m_xModifiable.is() ) + { + if ( !m_xModifiable->isSetModifiedEnabled() ) + { + // somebody already locked that, no need to lock it, again, and no need to unlock it later + m_xModifiable.clear(); + } + else + { + m_xModifiable->disableSetModified(); + } + } + } + + ~LockModifiable() + { + if ( m_xModifiable.is() ) + m_xModifiable->enableSetModified(); + } + + private: + Reference< XModifiable2 > m_xModifiable; + }; + + } + + // LifetimeCoupler + typedef ::cppu::WeakImplHelper< css::lang::XEventListener + > LifetimeCoupler_Base; + + namespace { + + /** helper class which couples the lifetime of a component to the lifetime + of another component + + Instances of this class are constructed with two components. The first is + simply held by reference, and thus kept alive. The second one is observed + for disposing calls - if they occur, i.e. if the component dies, + the reference to the first component is cleared. + + This way, you can ensure that a certain component is kept alive as long + as a second component is not disposed. + */ + class LifetimeCoupler : public LifetimeCoupler_Base + { + private: + Reference< XInterface > m_xClient; + + public: + static void couple( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor ) + { + new LifetimeCoupler( _rxClient, _rxActor ); + } + + private: + LifetimeCoupler( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor ) + :m_xClient( _rxClient ) + { + OSL_ENSURE( _rxActor.is(), "LifetimeCoupler::LifetimeCoupler: this will crash!" ); + osl_atomic_increment( &m_refCount ); + { + _rxActor->addEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + OSL_ENSURE( m_refCount, "LifetimeCoupler::LifetimeCoupler: the actor is not holding us by hard ref - this won't work!" ); + } + + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + protected: + }; + + } + + void SAL_CALL LifetimeCoupler::disposing( const css::lang::EventObject& /*Source*/ ) + { + m_xClient.clear(); + } + + namespace { + + // ODocumentSaveContinuation + class ODocumentSaveContinuation : public OInteraction< XInteractionDocumentSave > + { + OUString m_sName; + Reference m_xParentContainer; + + public: + ODocumentSaveContinuation() { } + + const Reference& getContent() const { return m_xParentContainer; } + const OUString& getName() const { return m_sName; } + + // XInteractionDocumentSave + virtual void SAL_CALL setName( const OUString& _sName,const Reference& _xParent) override; + }; + + } + + void SAL_CALL ODocumentSaveContinuation::setName( const OUString& _sName,const Reference& _xParent) + { + m_sName = _sName; + m_xParentContainer = _xParent; + } + +OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const Reference< XStorage >& _rxContainerStorage, + const OUString& _rEntityName, const Reference< XComponentContext >& _rContext, + Sequence< sal_Int8 >& _rClassId ) +{ + return GetDocumentServiceFromMediaType( + lcl_determineContentType_nothrow( _rxContainerStorage, _rEntityName ), + _rContext, _rClassId ); +} + +OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const OUString& _rMediaType, + const Reference< XComponentContext >& _rContext, Sequence< sal_Int8 >& _rClassId ) +{ + OUString sResult; + try + { + ::comphelper::MimeConfigurationHelper aConfigHelper( _rContext ); + sResult = aConfigHelper.GetDocServiceNameFromMediaType( _rMediaType ); + _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(aConfigHelper.GetExplicitlyRegisteredObjClassID( _rMediaType )); + if ( !_rClassId.hasElements() && !sResult.isEmpty() ) + { + Reference< XNameAccess > xObjConfig = aConfigHelper.GetObjConfiguration(); + if ( xObjConfig.is() ) + { + const Sequence< OUString > aClassIDs = xObjConfig->getElementNames(); + for ( OUString const & classId : aClassIDs ) + { + Reference< XNameAccess > xObjectProps; + OUString aEntryDocName; + + if ( ( xObjConfig->getByName( classId ) >>= xObjectProps ) && xObjectProps.is() + && ( xObjectProps->getByName("ObjectDocumentServiceName") >>= aEntryDocName ) + && aEntryDocName == sResult ) + { + _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(classId); + break; + } + } + } + } +#if OSL_DEBUG_LEVEL > 0 + // alternative, shorter approach + const Sequence< NamedValue > aProps( aConfigHelper.GetObjectPropsByMediaType( _rMediaType ) ); + const ::comphelper::NamedValueCollection aMediaTypeProps( aProps ); + const OUString sAlternativeResult = aMediaTypeProps.getOrDefault( "ObjectDocumentServiceName", OUString() ); + OSL_ENSURE( sAlternativeResult == sResult, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (1)!" ); + const Sequence< sal_Int8 > aAlternativeClassID = aMediaTypeProps.getOrDefault( "ClassID", Sequence< sal_Int8 >() ); + OSL_ENSURE( aAlternativeClassID == _rClassId, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (2)!" ); +#endif + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sResult; +} + +// ODocumentDefinition + +ODocumentDefinition::ODocumentDefinition( const Reference< XInterface >& _rxContainer, const Reference< XComponentContext >& _xORB, + const TContentPtr& _pImpl, bool _bForm ) + :OContentHelper(_xORB,_rxContainer,_pImpl) + ,OPropertyStateContainer(OContentHelper::rBHelper) + ,m_bForm(_bForm) + ,m_bOpenInDesign(false) + ,m_bInExecute(false) + ,m_bRemoveListener(false) +{ + registerProperties(); +} + +void ODocumentDefinition::initialLoad( const Sequence< sal_Int8 >& i_rClassID, const Sequence< PropertyValue >& i_rCreationArgs, + const Reference< XConnection >& i_rConnection ) +{ + OSL_ENSURE( i_rClassID.hasElements(), "ODocumentDefinition::initialLoad: illegal class ID!" ); + if ( !i_rClassID.hasElements() ) + return; + + loadEmbeddedObject( i_rConnection, i_rClassID, i_rCreationArgs, false, false ); +} + +ODocumentDefinition::~ODocumentDefinition() +{ + if ( !OContentHelper::rBHelper.bInDispose && !OContentHelper::rBHelper.bDisposed ) + { + acquire(); + dispose(); + } + + if ( m_pInterceptor.is() ) + { + m_pInterceptor->dispose(); + m_pInterceptor.clear(); + } +} + +void ODocumentDefinition::closeObject() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( m_xEmbeddedObject.is() ) + { + try + { + m_xEmbeddedObject->close(true); + } + catch(const Exception&) + { + } + m_xEmbeddedObject = nullptr; + m_pClientHelper.clear(); + } +} + +void SAL_CALL ODocumentDefinition::disposing() +{ + OContentHelper::disposing(); + ::osl::MutexGuard aGuard(m_aMutex); + closeObject(); + ::comphelper::disposeComponent(m_xListener); + if ( m_bRemoveListener ) + { + Reference xCloseable(m_pImpl->m_pDataSource->getModel_noCreate(),UNO_QUERY); + if ( xCloseable.is() ) + xCloseable->removeCloseListener(this); + } +} + +css::uno::Sequence ODocumentDefinition::getImplementationId() +{ + return css::uno::Sequence(); +} + +css::uno::Sequence< css::uno::Type > ODocumentDefinition::getTypes() +{ + return ::comphelper::concatSequences( + OContentHelper::getTypes( ), + OPropertyStateContainer::getTypes( ), + ODocumentDefinition_Base::getTypes( ) + ); +} +IMPLEMENT_FORWARD_XINTERFACE3( ODocumentDefinition,OContentHelper,OPropertyStateContainer,ODocumentDefinition_Base) + +void ODocumentDefinition::registerProperties() +{ + registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::CONSTRAINED | PropertyAttribute::BOUND | PropertyAttribute::READONLY, + &m_pImpl->m_aProps.aTitle, cppu::UnoTypem_aProps.aTitle)>::get()); + + registerProperty(PROPERTY_AS_TEMPLATE, PROPERTY_ID_AS_TEMPLATE, PropertyAttribute::READONLY, &m_pImpl->m_aProps.bAsTemplate, + cppu::UnoTypem_aProps.bAsTemplate)>::get()); + + registerProperty(PROPERTY_PERSISTENT_NAME, PROPERTY_ID_PERSISTENT_NAME, PropertyAttribute::READONLY, &m_pImpl->m_aProps.sPersistentName, + cppu::UnoTypem_aProps.sPersistentName)>::get()); + + registerProperty(PROPERTY_IS_FORM, PROPERTY_ID_IS_FORM, PropertyAttribute::READONLY, &m_bForm, cppu::UnoType::get()); +} + +void SAL_CALL ODocumentDefinition::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const +{ + if ( i_nHandle == PROPERTY_ID_PERSISTENT_PATH ) + { + OUString sPersistentPath; + if ( !m_pImpl->m_aProps.sPersistentName.isEmpty() ) + { + sPersistentPath = ODatabaseModelImpl::getObjectContainerStorageName( m_bForm ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + + "/" + m_pImpl->m_aProps.sPersistentName; + } + o_rValue <<= sPersistentPath; + return; + } + + OPropertyStateContainer::getFastPropertyValue( o_rValue, i_nHandle ); +} + +Reference< XPropertySetInfo > SAL_CALL ODocumentDefinition::getPropertySetInfo( ) +{ + Reference xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +IPropertyArrayHelper& ODocumentDefinition::getInfoHelper() +{ + return *getArrayHelper(); +} + +IPropertyArrayHelper* ODocumentDefinition::createArrayHelper( ) const +{ + // properties maintained by our base class (see registerProperties) + Sequence< Property > aProps; + describeProperties( aProps ); + + // properties not maintained by our base class + Sequence< Property > aManualProps{ { /* Name */ PROPERTY_PERSISTENT_PATH, + /* Handle */ PROPERTY_ID_PERSISTENT_PATH, + /* Type */ ::cppu::UnoType::get(), + /* Attributes */ PropertyAttribute::READONLY } }; + + return new OPropertyArrayHelper( ::comphelper::concatSequences( aProps, aManualProps ) ); +} + +namespace { + +class OExecuteImpl +{ + bool& m_rbSet; +public: + explicit OExecuteImpl(bool& _rbSet) : m_rbSet(_rbSet){ m_rbSet=true; } + ~OExecuteImpl(){ m_rbSet = false; } +}; + + bool lcl_extractOpenMode( const Any& _rValue, sal_Int32& _out_rMode ) + { + OpenCommandArgument aOpenCommand; + if ( _rValue >>= aOpenCommand ) + _out_rMode = aOpenCommand.Mode; + else + { + OpenCommandArgument2 aOpenCommand2; + if ( _rValue >>= aOpenCommand2 ) + _out_rMode = aOpenCommand2.Mode; + else + return false; + } + return true; + } +} + +void ODocumentDefinition::impl_removeFrameFromDesktop_throw( const Reference & _rxContext, const Reference< XFrame >& _rxFrame ) +{ + Reference< XDesktop2 > xDesktop = Desktop::create( _rxContext ); + Reference< XFrames > xFrames( xDesktop->getFrames(), UNO_SET_THROW ); + xFrames->remove( _rxFrame ); +} + +void ODocumentDefinition::impl_onActivateEmbeddedObject_nothrow( const bool i_bReactivated ) +{ + try + { + Reference< XModel > xModel( getComponent(), UNO_QUERY ); + Reference< XController > xController( xModel.is() ? xModel->getCurrentController() : Reference< XController >() ); + if ( !xController.is() ) + return; + + if ( !m_xListener.is() ) + // it's the first time the embedded object has been activated + // create an OEmbedObjectHolder + m_xListener = new OEmbedObjectHolder( m_xEmbeddedObject, this ); + + // raise the window to top (especially necessary if this is not the first activation) + Reference< XFrame > xFrame( xController->getFrame(), UNO_SET_THROW ); + Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + xTopWindow->toFront(); + + // remove the frame from the desktop's frame collection because we need full control of it. + impl_removeFrameFromDesktop_throw( m_aContext, xFrame ); + + // ensure that we ourself are kept alive as long as the embedded object's frame is + // opened + LifetimeCoupler::couple( *this, xFrame ); + + // init the edit view + if ( m_bForm && m_bOpenInDesign && !i_bReactivated ) + impl_initFormEditView( xController ); + } + catch( const RuntimeException& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +namespace +{ + // PreserveVisualAreaSize + /** stack-guard for preserving the size of the VisArea of an XModel + */ + class PreserveVisualAreaSize + { + private: + Reference< XVisualObject > m_xVisObject; + awt::Size m_aOriginalSize; + + public: + explicit PreserveVisualAreaSize( const Reference< XModel >& _rxModel ) + :m_xVisObject( _rxModel, UNO_QUERY ) + { + if ( m_xVisObject.is() ) + { + try + { + m_aOriginalSize = m_xVisObject->getVisualAreaSize( Aspects::MSOLE_CONTENT ); + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::PreserveVisualAreaSize" ); + } + } + } + + ~PreserveVisualAreaSize() + { + if ( m_xVisObject.is() && m_aOriginalSize.Width && m_aOriginalSize.Height ) + { + try + { + m_xVisObject->setVisualAreaSize( Aspects::MSOLE_CONTENT, m_aOriginalSize ); + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::~PreserveVisualAreaSize" ); + } + } + } + }; + + // LayoutManagerLock + /** helper class for stack-usage which during its lifetime locks a layout manager + */ + class LayoutManagerLock + { + private: + Reference< XLayoutManager > m_xLayoutManager; + + public: + explicit LayoutManagerLock( const Reference< XController >& _rxController ) + { + OSL_ENSURE( _rxController.is(), "LayoutManagerLock::LayoutManagerLock: this will crash!" ); + Reference< XFrame > xFrame( _rxController->getFrame() ); + try + { + Reference< XPropertySet > xPropSet( xFrame, UNO_QUERY_THROW ); + m_xLayoutManager.set( + xPropSet->getPropertyValue( "LayoutManager" ), + UNO_QUERY_THROW ); + m_xLayoutManager->lock(); + + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::LayoutManagerLock" ); + } + } + + ~LayoutManagerLock() + { + try + { + // unlock the layout manager + if ( m_xLayoutManager.is() ) + m_xLayoutManager->unlock(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::~LayoutManagerLock" ); + } + } + }; +} + +void ODocumentDefinition::impl_initFormEditView( const Reference< XController >& _rxController ) +{ + try + { + Reference< XViewSettingsSupplier > xSettingsSupplier( _rxController, UNO_QUERY_THROW ); + Reference< XPropertySet > xViewSettings( xSettingsSupplier->getViewSettings(), UNO_SET_THROW ); + + // the below code could indirectly tamper with the "modified" flag of the model, temporarily disable this + LockModifiable aLockModify( _rxController->getModel() ); + + // The visual area size can be changed by the setting of the following properties + // so it should be restored later + PreserveVisualAreaSize aPreserveVisAreaSize( _rxController->getModel() ); + + // Layout manager should not layout while the size is still not restored + // so it will stay locked for this time + LayoutManagerLock aLockLayout( _rxController ); + + // setting of the visual properties + xViewSettings->setPropertyValue("ShowRulers",Any(true)); + xViewSettings->setPropertyValue("ShowVertRuler",Any(true)); + xViewSettings->setPropertyValue("ShowHoriRuler",Any(true)); + xViewSettings->setPropertyValue("IsRasterVisible",Any(true)); + xViewSettings->setPropertyValue("IsSnapToRaster",Any(true)); + xViewSettings->setPropertyValue("ShowOnlineLayout",Any(true)); + xViewSettings->setPropertyValue("RasterSubdivisionX",Any(sal_Int32(5))); + xViewSettings->setPropertyValue("RasterSubdivisionY",Any(sal_Int32(5))); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODocumentDefinition::impl_showOrHideComponent_throw( const bool i_bShow ) +{ + const sal_Int32 nCurrentState = m_xEmbeddedObject.is() ? m_xEmbeddedObject->getCurrentState() : EmbedStates::LOADED; + switch ( nCurrentState ) + { + default: + case EmbedStates::LOADED: + throw embed::WrongStateException( OUString(), *this ); + + case EmbedStates::RUNNING: + if ( !i_bShow ) + // fine, a running (and not yet active) object is never visible + return; + { + LockModifiable aLockModify( impl_getComponent_throw() ); + m_xEmbeddedObject->changeState( EmbedStates::ACTIVE ); + impl_onActivateEmbeddedObject_nothrow( false ); + } + break; + + case EmbedStates::ACTIVE: + { + Reference< XModel > xEmbeddedDoc( impl_getComponent_throw(), UNO_QUERY_THROW ); + Reference< XController > xEmbeddedController( xEmbeddedDoc->getCurrentController(), UNO_SET_THROW ); + Reference< XFrame > xEmbeddedFrame( xEmbeddedController->getFrame(), UNO_SET_THROW ); + Reference< XWindow > xEmbeddedWindow( xEmbeddedFrame->getContainerWindow(), UNO_SET_THROW ); + xEmbeddedWindow->setVisible( i_bShow ); + } + break; + } +} + +Any ODocumentDefinition::onCommandOpenSomething( const Any& _rOpenArgument, const bool _bActivate, + const Reference< XCommandEnvironment >& _rxEnvironment ) +{ + OExecuteImpl aExecuteGuard( m_bInExecute ); + + Reference< XConnection > xConnection; + sal_Int32 nOpenMode = OpenMode::DOCUMENT; + + ::comphelper::NamedValueCollection aDocumentArgs; + + // for the document, default to the interaction handler as used for loading the DB doc + // This might be overwritten below, when examining _rOpenArgument. + const ::comphelper::NamedValueCollection& aDBDocArgs( m_pImpl->m_pDataSource->getMediaDescriptor() ); + Reference< XInteractionHandler > xHandler( aDBDocArgs.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() ) ); + if ( xHandler.is() ) + aDocumentArgs.put( "InteractionHandler", xHandler ); + + ::std::optional< sal_Int16 > aDocumentMacroMode; + + if ( !lcl_extractOpenMode( _rOpenArgument, nOpenMode ) ) + { + Sequence< PropertyValue > aArguments; + if ( _rOpenArgument >>= aArguments ) + { + const PropertyValue* pIter = aArguments.getConstArray(); + const PropertyValue* pEnd = pIter + aArguments.getLength(); + for ( ;pIter != pEnd; ++pIter ) + { + if ( pIter->Name == PROPERTY_ACTIVE_CONNECTION ) + { + xConnection.set( pIter->Value, UNO_QUERY ); + continue; + } + + if ( lcl_extractOpenMode( pIter->Value, nOpenMode ) ) + continue; + + if ( pIter->Name == "MacroExecutionMode" ) + { + sal_Int16 nMacroExecMode( !aDocumentMacroMode ? MacroExecMode::USE_CONFIG : *aDocumentMacroMode ); + OSL_VERIFY( pIter->Value >>= nMacroExecMode ); + aDocumentMacroMode = nMacroExecMode; + continue; + } + + // unknown argument -> pass to the loaded document + aDocumentArgs.put( pIter->Name, pIter->Value ); + } + } + } + + bool bExecuteDBDocMacros = m_pImpl->m_pDataSource->checkMacrosOnLoading(); + // Note that this call implies the user might be asked for the macro execution mode. + // Normally, this would happen when the database document is loaded, and subsequent calls + // will simply use the user's decision from this point in time. + // However, it is possible to programmatically load forms/reports, without actually + // loading the database document into a frame. In this case, the user will be asked + // here and now. + // #i87741# + + // allow the command arguments to downgrade the macro execution mode, but not to upgrade + // it + if ( ( m_pImpl->m_pDataSource->getImposedMacroExecMode() == MacroExecMode::USE_CONFIG ) + && bExecuteDBDocMacros + ) + { + // while loading the whole database document, USE_CONFIG, was passed. + // Additionally, *by now* executing macros from the DB doc is allowed (this is what bExecuteDBDocMacros + // indicates). This means either one of: + // 1. The DB doc or one of the sub docs contained macros and + // 1a. the user explicitly allowed executing them + // 1b. the configuration allows executing them without asking the user + // 2. Neither the DB doc nor the sub docs contained macros, thus macro + // execution was silently enabled, assuming that any macro will be a + // user-created macro + // + // The problem with this: If the to-be-opened sub document has macros embedded in + // the content.xml (which is valid ODF, but normally not produced by OOo itself), + // then this has not been detected while loading the database document - it would + // be too expensive, as it effectively would require loading all forms/reports. + // + // So, in such a case, and with 2. above, we would silently execute those macros, + // regardless of the global security settings - which would be a security issue, of + // course. + if ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::eNoMacros ) + { + // this is case 2. from above + // So, pass a USE_CONFIG to the to-be-loaded document. This means that + // the user will be prompted with a security message upon opening this + // sub document, in case the settings require this, *and* the document + // contains scripts in the content.xml. But this is better than the security + // issue we had before ... + aDocumentMacroMode = MacroExecMode::USE_CONFIG; + } + } + + if ( !aDocumentMacroMode ) + { + // nobody so far felt responsible for setting it + // => use the DBDoc-wide macro exec mode for the document, too + aDocumentMacroMode = bExecuteDBDocMacros ? MacroExecMode::ALWAYS_EXECUTE_NO_WARN + : MacroExecMode::NEVER_EXECUTE; + } + aDocumentArgs.put( "MacroExecutionMode", *aDocumentMacroMode ); + + if ( ( nOpenMode == OpenMode::ALL ) + || ( nOpenMode == OpenMode::FOLDERS ) + || ( nOpenMode == OpenMode::DOCUMENTS ) + || ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_NONE ) + || ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_WRITE ) + ) + { + // not supported + ucbhelper::cancelCommandExecution( + Any( UnsupportedOpenModeException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + sal_Int16( nOpenMode ) ) ), + _rxEnvironment ); + // Unreachable + OSL_FAIL( "unreachable" ); + } + + OSL_ENSURE( !m_pImpl->m_aProps.sPersistentName.isEmpty(), + "ODocumentDefinition::onCommandOpenSomething: no persistent name - cannot load!" ); + if ( m_pImpl->m_aProps.sPersistentName.isEmpty() ) + return Any(); + + // embedded objects themself do not support the hidden flag. We implement support for + // it by changing the STATE to RUNNING only, instead of ACTIVE. + bool bOpenHidden = aDocumentArgs.getOrDefault( "Hidden", false ); + aDocumentArgs.remove( "Hidden" ); + + loadEmbeddedObject( xConnection, Sequence< sal_Int8 >(), aDocumentArgs.getPropertyValues(), false, !m_bOpenInDesign ); + OSL_ENSURE( m_xEmbeddedObject.is(), "ODocumentDefinition::onCommandOpenSomething: what's this?" ); + if ( !m_xEmbeddedObject.is() ) + return Any(); + + Reference< XModel > xModel( getComponent(), UNO_QUERY ); + Reference< report::XReportDefinition > xReportDefinition(xModel,UNO_QUERY); + + Reference< XModule > xModule( xModel, UNO_QUERY ); + if ( xModule.is() ) + { + if ( m_bForm ) + xModule->setIdentifier( "com.sun.star.sdb.FormDesign" ); + else if ( !xReportDefinition.is() ) + xModule->setIdentifier( "com.sun.star.text.TextDocument" ); + + updateDocumentTitle(); + } + + bool bIsAliveNewStyleReport = ( !m_bOpenInDesign && xReportDefinition.is() ); + if ( bIsAliveNewStyleReport ) + { + // we are in ReadOnly mode + // we would like to open the Writer or Calc with the report direct, without design it. + Reference< report::XReportEngine > xReportEngine( m_aContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.report.OReportEngineJFree", m_aContext), UNO_QUERY_THROW ); + + xReportEngine->setReportDefinition(xReportDefinition); + xReportEngine->setActiveConnection(m_xLastKnownConnection); + if ( bOpenHidden ) + return Any( xReportEngine->createDocumentModel() ); + return Any( xReportEngine->createDocumentAlive( nullptr ) ); + } + + if ( _bActivate && !bOpenHidden ) + { + LockModifiable aLockModify( impl_getComponent_throw() ); + m_xEmbeddedObject->changeState( EmbedStates::ACTIVE ); + impl_onActivateEmbeddedObject_nothrow( false ); + } + else + { + // ensure that we ourself are kept alive as long as the document is open + LifetimeCoupler::couple( *this, xModel ); + } + + if ( !m_bForm && m_pImpl->m_aProps.bAsTemplate && !m_bOpenInDesign ) + ODocumentDefinition::fillReportData( m_aContext, getComponent(), xConnection ); + + return Any( xModel ); +} + +Any SAL_CALL ODocumentDefinition::execute( const Command& aCommand, sal_Int32 CommandId, const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + + bool bOpen = aCommand.Name == "open"; + bool bOpenInDesign = aCommand.Name == "openDesign"; + bool bOpenForMail = aCommand.Name == "openForMail"; + if ( bOpen || bOpenInDesign || bOpenForMail ) + { + // opening the document involves a lot of VCL code, which is not thread-safe, but needs the SolarMutex locked. + // Unfortunately, the DocumentDefinition, as well as the EmbeddedObject implementation, calls into VCL-dependent + // components *without* releasing the own mutex, which is a guaranteed recipe for deadlocks. + // We have control over this implementation here, and in modifying it to release the own mutex before calling into + // the VCL-dependent components is not too difficult (was there, seen it). + // However, we do /not/ have control over the EmbeddedObject implementation, and from a first look, it seems as + // making it release the own mutex before calling SolarMutex-code is ... difficult, at least. + // So, to be on the same side, we lock the SolarMutex here. Yes, it sucks. + ::SolarMutexGuard aSolarGuard; + osl::MutexGuard aGuard(m_aMutex); + if ( m_bInExecute ) + return aRet; + + bool bActivateObject = true; + if ( bOpenForMail ) + { + OSL_FAIL( "ODocumentDefinition::execute: 'openForMail' should not be used anymore - use the 'Hidden' parameter instead!" ); + bActivateObject = false; + } + + // if the object is already opened, do nothing + if ( m_xEmbeddedObject.is() ) + { + sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState(); + bool bIsActive = ( nCurrentState == EmbedStates::ACTIVE ); + + if ( bIsActive ) + { + // exception: new-style reports always create a new document when "open" is executed + Reference< report::XReportDefinition > xReportDefinition( impl_getComponent_throw( false ), UNO_QUERY ); + bool bIsAliveNewStyleReport = ( xReportDefinition.is() && ( bOpen || bOpenForMail ) ); + + if ( !bIsAliveNewStyleReport ) + { + impl_onActivateEmbeddedObject_nothrow( true ); + return Any( getComponent() ); + } + } + } + + m_bOpenInDesign = bOpenInDesign || bOpenForMail; + return onCommandOpenSomething( aCommand.Argument, bActivateObject, Environment ); + } + + osl::MutexGuard aGuard(m_aMutex); + if ( m_bInExecute ) + return aRet; + + if ( aCommand.Name == "copyTo" ) + { + Sequence aIni; + aCommand.Argument >>= aIni; + if ( aIni.getLength() != 2 ) + { + OSL_FAIL( "Wrong argument type!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + Reference< XStorage> xDest(aIni[0],UNO_QUERY); + OUString sPersistentName; + aIni[1] >>= sPersistentName; + Reference< XStorage> xStorage = getContainerStorage(); + + xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xDest,sPersistentName); + } + else if ( aCommand.Name == "preview" ) + { + onCommandPreview(aRet); + } + else if ( aCommand.Name == "insert" ) + { + Sequence aIni; + aCommand.Argument >>= aIni; + if ( !aIni.hasElements() ) + { + OSL_FAIL( "Wrong argument count!" ); + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + OUString sURL; + aIni[0] >>= sURL; + onCommandInsert( sURL, Environment ); + } + else if ( aCommand.Name == "getdocumentinfo" // compatibility + || aCommand.Name == "getDocumentInfo" ) + { + onCommandGetDocumentProperties( aRet ); + } + else if ( aCommand.Name == "delete" ) + { + // delete + closeObject(); + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + xStorage->removeElement(m_pImpl->m_aProps.sPersistentName); + + dispose(); + + } + else if ( aCommand.Name == "storeOwn" // compatibility + || aCommand.Name == "store" + ) + { + impl_store_throw(); + } + else if ( aCommand.Name == "shutdown" // compatibility + || aCommand.Name == "close" + ) + { + aRet <<= impl_close_throw(); + } + else if ( aCommand.Name == "show" ) + { + impl_showOrHideComponent_throw( true ); + } + else if ( aCommand.Name == "hide" ) + { + impl_showOrHideComponent_throw( false ); + } + else + { + aRet = OContentHelper::execute(aCommand,CommandId,Environment); + } + + return aRet; +} + +namespace +{ + void lcl_resetChildFormsToEmptyDataSource( const Reference< XIndexAccess>& _rxFormsContainer ) + { + OSL_PRECOND( _rxFormsContainer.is(), "lcl_resetChildFormsToEmptyDataSource: illegal call!" ); + sal_Int32 count = _rxFormsContainer->getCount(); + for ( sal_Int32 i = 0; i < count; ++i ) + { + Reference< XForm > xForm( _rxFormsContainer->getByIndex( i ), UNO_QUERY ); + if ( !xForm.is() ) + continue; + + // if the element is a form, reset its DataSourceName property to an empty string + try + { + Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW ); + xFormProps->setPropertyValue( PROPERTY_DATASOURCENAME, Any( OUString() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // if the element is a container itself, step down the component hierarchy + Reference< XIndexAccess > xContainer( xForm, UNO_QUERY ); + if ( xContainer.is() ) + lcl_resetChildFormsToEmptyDataSource( xContainer ); + } + } + + void lcl_resetFormsToEmptyDataSource( const Reference< XEmbeddedObject>& _rxEmbeddedObject ) + { + try + { + Reference< XDrawPageSupplier > xSuppPage( _rxEmbeddedObject->getComponent(), UNO_QUERY_THROW ); + // if this interface does not exist, then either getComponent returned NULL, + // or the document is a multi-page document. The latter is allowed, but currently + // simply not handled by this code, as it would not normally happen. + + Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW ); + lcl_resetChildFormsToEmptyDataSource( xForms ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + } +} + +void ODocumentDefinition::onCommandInsert( const OUString& _sURL, const Reference< XCommandEnvironment >& Environment ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + + // Check, if all required properties were set. + if ( _sURL.isEmpty() || m_xEmbeddedObject.is() ) + { + OSL_FAIL( "Content::onCommandInsert - property value missing!" ); + + Sequence aProps { PROPERTY_URL }; + ucbhelper::cancelCommandExecution( + Any( MissingPropertiesException( + OUString(), + static_cast< cppu::OWeakObject * >( this ), + aProps ) ), + Environment ); + // Unreachable + } + + if ( !m_xEmbeddedObject.is() ) + { + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + { + Reference< XEmbeddedObjectCreator> xEmbedFactory = EmbeddedObjectCreator::create(m_aContext); + Sequence aEmpty; + Sequence aMediaDesc{ comphelper::makePropertyValue(PROPERTY_URL, + _sURL) }; + m_xEmbeddedObject.set(xEmbedFactory->createInstanceInitFromMediaDescriptor( xStorage + ,m_pImpl->m_aProps.sPersistentName + ,aMediaDesc + ,aEmpty),UNO_QUERY); + + lcl_resetFormsToEmptyDataSource( m_xEmbeddedObject ); + // #i57669# + + Reference xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + } + try + { + if ( m_xEmbeddedObject.is() ) + m_xEmbeddedObject->close(true); + } + catch(const Exception&) + { + } + m_xEmbeddedObject = nullptr; + } + } + + aGuard.clear(); +} + +bool ODocumentDefinition::save(bool _bApprove, const css::uno::Reference& rDialogParent) +{ + // default handling: instantiate an interaction handler and let it handle the parameter request + if ( !m_bOpenInDesign ) + return false; + try + { + + { + ::SolarMutexGuard aSolarGuard; + + // the request + Reference xName(m_xParentContainer,UNO_QUERY); + DocumentSaveRequest aRequest; + aRequest.Name = m_pImpl->m_aProps.aTitle; + if ( aRequest.Name.isEmpty() ) + { + if ( m_bForm ) + aRequest.Name = DBA_RES( RID_STR_FORM ); + else + aRequest.Name = DBA_RES( RID_STR_REPORT ); + aRequest.Name = ::dbtools::createUniqueName(xName,aRequest.Name); + } + + aRequest.Content.set(m_xParentContainer,UNO_QUERY); + rtl::Reference pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + // two continuations allowed: OK and Cancel + rtl::Reference pDocuSave; + + if ( m_pImpl->m_aProps.aTitle.isEmpty() ) + { + pDocuSave = new ODocumentSaveContinuation; + pRequest->addContinuation(pDocuSave); + } + if ( _bApprove ) + { + rtl::Reference> pApprove = new OInteraction< XInteractionApprove >; + pRequest->addContinuation(pApprove); + } + + rtl::Reference> pDisApprove = new OInteraction< XInteractionDisapprove >; + pRequest->addContinuation(pDisApprove); + + rtl::Reference pAbort = new OInteractionAbort; + pRequest->addContinuation(pAbort); + + Reference xDialogParent(rDialogParent, UNO_QUERY); + + // create the handler, let it handle the request + Reference xHandler(InteractionHandler::createWithParent(m_aContext, xDialogParent)); + xHandler->handle(pRequest); + + if ( pAbort->wasSelected() ) + return false; + if ( pDisApprove->wasSelected() ) + return true; + if ( pDocuSave && pDocuSave->wasSelected() ) + { + Reference xNC( pDocuSave->getContent(), UNO_QUERY_THROW ); + + ::osl::ResettableMutexGuard aGuard( m_aMutex ); + NameChangeNotifier aNameChangeAndNotify( *this, pDocuSave->getName(), aGuard ); + m_pImpl->m_aProps.aTitle = pDocuSave->getName(); + + Reference< XContent> xContent = this; + xNC->insertByName(pDocuSave->getName(),Any(xContent)); + + updateDocumentTitle(); + } + } + + ::osl::MutexGuard aGuard(m_aMutex); + Reference xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!"); + } + return true; +} + +void ODocumentDefinition::saveAs() +{ + // default handling: instantiate an interaction handler and let it handle the parameter request + if ( !m_bOpenInDesign ) + return; + + { + osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex ); + if ( m_pImpl->m_aProps.aTitle.isEmpty() ) + { + aGuard.clear(); + save(false, css::uno::Reference()); // (sal_False) : we don't want an approve dialog + return; + } + } + try + { + ::SolarMutexGuard aSolarGuard; + + // the request + DocumentSaveRequest aRequest; + aRequest.Name = m_pImpl->m_aProps.aTitle; + + aRequest.Content.set(m_xParentContainer,UNO_QUERY); + rtl::Reference pRequest = new OInteractionRequest(Any(aRequest)); + // some knittings + // two continuations allowed: OK and Cancel + rtl::Reference pDocuSave = new ODocumentSaveContinuation; + pRequest->addContinuation(pDocuSave); + rtl::Reference> pDisApprove = new OInteraction< XInteractionDisapprove >; + pRequest->addContinuation(pDisApprove); + rtl::Reference pAbort = new OInteractionAbort; + pRequest->addContinuation(pAbort); + + // create the handler, let it handle the request + Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) ); + xHandler->handle(pRequest); + + if ( pAbort->wasSelected() ) + return; + if ( pDisApprove->wasSelected() ) + return; + if ( pDocuSave->wasSelected() ) + { + ::osl::MutexGuard aGuard(m_aMutex); + Reference xNC(pDocuSave->getContent(),UNO_QUERY); + if ( xNC.is() ) + { + if ( m_pImpl->m_aProps.aTitle != pDocuSave->getName() ) + { + try + { + Reference< XStorage> xStorage = getContainerStorage(); + + OUString sPersistentName = ::dbtools::createUniqueName(xStorage,"Obj"); + xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xStorage,sPersistentName); + + OUString sOldName = m_pImpl->m_aProps.aTitle; + rename(pDocuSave->getName()); + updateDocumentTitle(); + + uno::Sequence aArguments(comphelper::InitAnyPropertySequence( + { + {PROPERTY_NAME, uno::Any(sOldName)}, // set as folder + {PROPERTY_PERSISTENT_NAME, uno::Any(sPersistentName)}, + {PROPERTY_AS_TEMPLATE, uno::Any(m_pImpl->m_aProps.bAsTemplate)}, + })); + Reference< XMultiServiceFactory > xORB( m_xParentContainer, UNO_QUERY_THROW ); + Reference< XInterface > xComponent( xORB->createInstanceWithArguments( SERVICE_SDB_DOCUMENTDEFINITION, aArguments ) ); + Reference< XNameContainer > xNameContainer( m_xParentContainer, UNO_QUERY_THROW ); + xNameContainer->insertByName( sOldName, Any( xComponent ) ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + Reference xPersist(m_xEmbeddedObject,UNO_QUERY); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } + } + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!"); + } +} + +namespace +{ + void lcl_putLoadArgs( ::comphelper::NamedValueCollection& _io_rArgs, const optional_bool& _bSuppressMacros, const optional_bool& _bReadOnly ) + { + if ( !!_bSuppressMacros ) + { + if ( *_bSuppressMacros ) + { + // if we're to suppress macros, do exactly this + _io_rArgs.put( "MacroExecutionMode", MacroExecMode::NEVER_EXECUTE ); + } + else + { + // otherwise, put the setting only if not already present + if ( !_io_rArgs.has( "MacroExecutionMode" ) ) + { + _io_rArgs.put( "MacroExecutionMode", MacroExecMode::USE_CONFIG ); + } + } + } + + if ( !!_bReadOnly ) + _io_rArgs.put( "ReadOnly", *_bReadOnly ); + } +} + +namespace +{ + Reference< XFrame > lcl_getDatabaseDocumentFrame( ODatabaseModelImpl const & _rImpl ) + { + Reference< XModel > xDatabaseDocumentModel( _rImpl.getModel_noCreate() ); + + Reference< XController > xDatabaseDocumentController; + if ( xDatabaseDocumentModel.is() ) + xDatabaseDocumentController = xDatabaseDocumentModel->getCurrentController(); + + Reference< XFrame > xFrame; + if ( xDatabaseDocumentController.is() ) + xFrame = xDatabaseDocumentController->getFrame(); + + return xFrame; + } +} + +bool ODocumentDefinition::objectSupportsEmbeddedScripts() const +{ + bool bAllowDocumentMacros = !m_pImpl->m_pDataSource + || ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::eSubDocumentMacros ); + + // if *any* of the objects of the database document already has macros, we + // continue to allow it to have them, until the user does a migration. + // If there are no macros, we don't allow them to be created. + + return bAllowDocumentMacros; +} + +OUString ODocumentDefinition::determineContentType() const +{ + return lcl_determineContentType_nothrow( getContainerStorage(), m_pImpl->m_aProps.sPersistentName ); +} + +void ODocumentDefinition::separateOpenCommandArguments( const Sequence< PropertyValue >& i_rOpenCommandArguments, + ::comphelper::NamedValueCollection& o_rDocumentLoadArgs, ::comphelper::NamedValueCollection& o_rEmbeddedObjectDescriptor ) +{ + ::comphelper::NamedValueCollection aOpenCommandArguments( i_rOpenCommandArguments ); + + static const std::u16string_view sObjectDescriptorArgs[] = { u"RecoveryStorage" }; + for (const auto& rObjectDescriptorArg : sObjectDescriptorArgs) + { + const OUString sObjectDescriptorArg(rObjectDescriptorArg); + if ( aOpenCommandArguments.has( sObjectDescriptorArg ) ) + { + o_rEmbeddedObjectDescriptor.put( sObjectDescriptorArg, aOpenCommandArguments.get( sObjectDescriptorArg ) ); + aOpenCommandArguments.remove( sObjectDescriptorArg ); + } + } + + o_rDocumentLoadArgs.merge( aOpenCommandArguments, false ); +} + +Sequence< PropertyValue > ODocumentDefinition::fillLoadArgs( const Reference< XConnection>& _xConnection, const bool _bSuppressMacros, const bool _bReadOnly, + const Sequence< PropertyValue >& i_rOpenCommandArguments, Sequence< PropertyValue >& _out_rEmbeddedObjectDescriptor ) +{ + // (re-)create interceptor, and put it into the descriptor of the embedded object + if ( m_pInterceptor.is() ) + { + m_pInterceptor->dispose(); + m_pInterceptor.clear(); + } + + m_pInterceptor = new OInterceptor( this ); + Reference xInterceptor = m_pInterceptor; + + ::comphelper::NamedValueCollection aEmbeddedDescriptor; + aEmbeddedDescriptor.put( "OutplaceDispatchInterceptor", xInterceptor ); + + ::comphelper::NamedValueCollection aMediaDesc; + separateOpenCommandArguments( i_rOpenCommandArguments, aMediaDesc, aEmbeddedDescriptor ); + + // create the OutplaceFrameProperties, and put them into the descriptor of the embedded object + ::comphelper::NamedValueCollection OutplaceFrameProperties; + OutplaceFrameProperties.put( "TopWindow", true ); + OutplaceFrameProperties.put( "SupportPersistentWindowState", true ); + + Reference< XFrame > xParentFrame; + if ( m_pImpl->m_pDataSource ) + xParentFrame = lcl_getDatabaseDocumentFrame( *m_pImpl->m_pDataSource ); + if ( !xParentFrame.is() ) + { // i87957 we need a parent frame + Reference< XDesktop2 > xDesktop = Desktop::create( m_aContext ); + xParentFrame.set( xDesktop, UNO_QUERY_THROW ); + Reference xCloseable(m_pImpl->m_pDataSource->getModel_noCreate(),UNO_QUERY); + if ( xCloseable.is() ) + { + xCloseable->addCloseListener(this); + m_bRemoveListener = true; + } + } + OSL_ENSURE( xParentFrame.is(), "ODocumentDefinition::fillLoadArgs: no parent frame!" ); + if ( xParentFrame.is() ) + OutplaceFrameProperties.put( "ParentFrame", xParentFrame ); + + aEmbeddedDescriptor.put( "OutplaceFrameProperties", OutplaceFrameProperties.getNamedValues() ); + + // tell the embedded object to have (or not have) script support + aEmbeddedDescriptor.put( "EmbeddedScriptSupport", objectSupportsEmbeddedScripts() ); + + // tell the embedded object to not participate in the document recovery game - the DB doc will handle it + aEmbeddedDescriptor.put( "DocumentRecoverySupport", false ); + + // pass the descriptor of the embedded object to the caller + aEmbeddedDescriptor >>= _out_rEmbeddedObjectDescriptor; + + // create the ComponentData, and put it into the document's media descriptor + { + ::comphelper::NamedValueCollection aComponentData; + aComponentData.put( "ActiveConnection", _xConnection ); + aComponentData.put( "ApplyFormDesignMode", !_bReadOnly ); + aMediaDesc.put( "ComponentData", aComponentData.getPropertyValues() ); + } + + if ( !m_pImpl->m_aProps.aTitle.isEmpty() ) + aMediaDesc.put( "DocumentTitle", m_pImpl->m_aProps.aTitle ); + + aMediaDesc.put( "DocumentBaseURL", m_pImpl->m_pDataSource->getURL() ); + + // put the common load arguments into the document's media descriptor + lcl_putLoadArgs( aMediaDesc, optional_bool( _bSuppressMacros ), optional_bool( _bReadOnly ) ); + + return aMediaDesc.getPropertyValues(); +} + +void ODocumentDefinition::loadEmbeddedObject( const Reference< XConnection >& i_rConnection, const Sequence< sal_Int8 >& _aClassID, + const Sequence< PropertyValue >& i_rOpenCommandArguments, const bool _bSuppressMacros, const bool _bReadOnly ) +{ + if ( !m_xEmbeddedObject.is() ) + { + Reference< XStorage> xStorage = getContainerStorage(); + if ( xStorage.is() ) + { + Reference< XEmbeddedObjectCreator> xEmbedFactory = OOoEmbeddedObjectFactory::create(m_aContext); + OUString sDocumentService; + bool bSetSize = false; + sal_Int32 nEntryConnectionMode = EntryInitModes::DEFAULT_INIT; + Sequence< sal_Int8 > aClassID = _aClassID; + if ( aClassID.hasElements() ) + { + nEntryConnectionMode = EntryInitModes::TRUNCATE_INIT; + bSetSize = true; + } + else + { + sDocumentService = GetDocumentServiceFromMediaType( getContentType(), m_aContext, aClassID ); + // check if we are not a form and + // the org.libreoffice.report.pentaho.SOReportJobFactory is not present. + if ( !m_bForm && sDocumentService != "com.sun.star.text.TextDocument") + { + // we seem to be a "new style" report, check if report extension is present. + Reference< XContentEnumerationAccess > xEnumAccess( m_aContext->getServiceManager(), UNO_QUERY ); + const OUString sReportEngineServiceName = ::dbtools::getDefaultReportEngineServiceName(m_aContext); + Reference< XEnumeration > xEnumDrivers = xEnumAccess->createContentEnumeration(sReportEngineServiceName); + if ( !xEnumDrivers.is() || !xEnumDrivers->hasMoreElements() ) + { + css::io::WrongFormatException aWFE; + aWFE.Message = DBA_RES( RID_STR_MISSING_EXTENSION ); + throw aWFE; + } + } + if ( !aClassID.hasElements() ) + { + if ( m_bForm ) + aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_SW_CLASSID); + else + { + aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_RPT_CLASSID_90); + } + } + } + + OSL_ENSURE( aClassID.hasElements(),"No Class ID" ); + + Sequence< PropertyValue > aEmbeddedObjectDescriptor; + Sequence< PropertyValue > aLoadArgs( fillLoadArgs( + i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) ); + + m_xEmbeddedObject.set(xEmbedFactory->createInstanceUserInit(aClassID + ,sDocumentService + ,xStorage + ,m_pImpl->m_aProps.sPersistentName + ,nEntryConnectionMode + ,aLoadArgs + ,aEmbeddedObjectDescriptor + ),UNO_QUERY); + if ( m_xEmbeddedObject.is() ) + { + if ( !m_pClientHelper.is() ) + { + m_pClientHelper = new OEmbeddedClientHelper; + } + m_xEmbeddedObject->setClientSite(m_pClientHelper); + m_xEmbeddedObject->changeState(EmbedStates::RUNNING); + if ( bSetSize ) + { + LockModifiable aLockModify( impl_getComponent_throw( false ) ); + + awt::Size aSize( DEFAULT_WIDTH, DEFAULT_HEIGHT ); + m_xEmbeddedObject->setVisualAreaSize(Aspects::MSOLE_CONTENT,aSize); + } + } + } + } + else + { + sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState(); + if ( nCurrentState == EmbedStates::LOADED ) + { + if ( !m_pClientHelper.is() ) + { + m_pClientHelper = new OEmbeddedClientHelper; + } + m_xEmbeddedObject->setClientSite(m_pClientHelper); + + Sequence< PropertyValue > aEmbeddedObjectDescriptor; + Sequence< PropertyValue > aLoadArgs( fillLoadArgs( + i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) ); + + Reference xCommon(m_xEmbeddedObject,UNO_QUERY); + OSL_ENSURE(xCommon.is(),"unsupported interface!"); + if ( xCommon.is() ) + xCommon->reload( aLoadArgs, aEmbeddedObjectDescriptor ); + m_xEmbeddedObject->changeState(EmbedStates::RUNNING); + } + else + { + OSL_ENSURE( ( nCurrentState == EmbedStates::RUNNING ) || ( nCurrentState == EmbedStates::ACTIVE ), + "ODocumentDefinition::loadEmbeddedObject: unexpected state!" ); + + // if the document was already loaded (which means the embedded object is in state RUNNING or ACTIVE), + // then just re-set some model parameters + try + { + // ensure the media descriptor doesn't contain any values which are intended for the + // EmbeddedObjectDescriptor only + ::comphelper::NamedValueCollection aEmbeddedObjectDescriptor; + ::comphelper::NamedValueCollection aNewMediaDesc; + separateOpenCommandArguments( i_rOpenCommandArguments, aNewMediaDesc, aEmbeddedObjectDescriptor ); + + // merge the new media descriptor into the existing media descriptor + const Reference< XModel > xModel( getComponent(), UNO_QUERY_THROW ); + const Sequence< PropertyValue > aArgs = xModel->getArgs(); + ::comphelper::NamedValueCollection aExistentMediaDesc( aArgs ); + aExistentMediaDesc.merge( aNewMediaDesc, false ); + + lcl_putLoadArgs( aExistentMediaDesc, optional_bool(), optional_bool() ); + // don't put _bSuppressMacros and _bReadOnly here - if the document was already + // loaded, we should not tamper with its settings. + // #i88977# #i86872# + + xModel->attachResource( xModel->getURL(), aExistentMediaDesc.getPropertyValues() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + // set the OfficeDatabaseDocument instance as parent of the embedded document + // #i40358# + Reference< XChild > xDepdendDocAsChild( getComponent(), UNO_QUERY ); + if ( xDepdendDocAsChild.is() ) + { + try + { + if ( !xDepdendDocAsChild->getParent().is() ) + { // first encounter + xDepdendDocAsChild->setParent( getDataSource( m_xParentContainer ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + if ( i_rConnection.is() ) + m_xLastKnownConnection = i_rConnection; +} + +void ODocumentDefinition::onCommandPreview(Any& _rImage) +{ + loadEmbeddedObjectForPreview(); + if ( !m_xEmbeddedObject.is() ) + return; + + try + { + Reference xTransfer(getComponent(),UNO_QUERY); + if ( xTransfer.is() ) + { + DataFlavor aFlavor; + aFlavor.MimeType = "image/png"; + aFlavor.HumanPresentableName = "Portable Network Graphics"; + aFlavor.DataType = cppu::UnoType>::get(); + + _rImage = xTransfer->getTransferData( aFlavor ); + } + } + catch( const Exception& ) + { + } +} + +void ODocumentDefinition::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const +{ + _rDefault.clear(); +} + +void ODocumentDefinition::onCommandGetDocumentProperties( Any& _rProps ) +{ + loadEmbeddedObjectForPreview(); + if ( !m_xEmbeddedObject.is() ) + return; + + try + { + Reference xDocSup( + getComponent(), UNO_QUERY ); + if ( xDocSup.is() ) + _rProps <<= xDocSup->getDocumentProperties(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +Reference< util::XCloseable > ODocumentDefinition::impl_getComponent_throw( const bool i_ForceCreate ) +{ + OSL_ENSURE(m_xEmbeddedObject.is(),"Illegal call for embeddedObject"); + Reference< util::XCloseable > xComp; + if ( m_xEmbeddedObject.is() ) + { + int nState = m_xEmbeddedObject->getCurrentState(); + if ( ( nState == EmbedStates::LOADED ) && i_ForceCreate ) + { + m_xEmbeddedObject->changeState( EmbedStates::RUNNING ); + nState = m_xEmbeddedObject->getCurrentState(); + OSL_ENSURE( nState == EmbedStates::RUNNING, "ODocumentDefinition::impl_getComponent_throw: could not switch to RUNNING!" ); + } + + if ( nState == EmbedStates::ACTIVE || nState == EmbedStates::RUNNING ) + { + if ( m_xEmbeddedObject.is() ) + { + xComp = m_xEmbeddedObject->getComponent(); + OSL_ENSURE(xComp.is(),"No valid component"); + } + } + } + return xComp; +} + +Reference< util::XCloseable > ODocumentDefinition::getComponent() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getComponent_throw(); +} + +namespace +{ + Reference< XDatabaseDocumentUI > lcl_getDatabaseDocumentUI( ODatabaseModelImpl const & _rModelImpl ) + { + Reference< XDatabaseDocumentUI > xUI; + + Reference< XModel > xModel( _rModelImpl.getModel_noCreate() ); + if ( xModel.is() ) + xUI.set( xModel->getCurrentController(), UNO_QUERY ); + return xUI; + } +} + +Reference< XComponent > ODocumentDefinition::impl_openUI_nolck_throw( bool _bForEditing ) +{ + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( !m_pImpl || !m_pImpl->m_pDataSource ) + throw DisposedException(); + + Reference< XComponent > xComponent; + try + { + Reference< XDatabaseDocumentUI > xUI( lcl_getDatabaseDocumentUI( *m_pImpl->m_pDataSource ) ); + if ( !xUI.is() ) + { + // no XDatabaseDocumentUI -> just execute the respective command + m_bOpenInDesign = _bForEditing; + xComponent = Reference(onCommandOpenSomething(Any(), true, nullptr), UNO_QUERY); + OSL_ENSURE( xComponent.is(), "ODocumentDefinition::impl_openUI_nolck_throw: opening the thingie failed." ); + return xComponent; + } + + + OUString sName( impl_getHierarchicalName( false ) ); + sal_Int32 nObjectType = m_bForm ? DatabaseObject::FORM : DatabaseObject::REPORT; + aGuard.clear(); + + xComponent = xUI->loadComponent( + nObjectType, sName, _bForEditing + ); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } + + return xComponent; +} + +void ODocumentDefinition::impl_store_throw() +{ + Reference xPersist( m_xEmbeddedObject, UNO_QUERY ); + if ( xPersist.is() ) + { + xPersist->storeOwn(); + notifyDataSourceModified(); + } +} + +bool ODocumentDefinition::impl_close_throw() +{ + bool bSuccess = prepareClose(); + if ( bSuccess && m_xEmbeddedObject.is() ) + { + m_xEmbeddedObject->changeState( EmbedStates::LOADED ); + bSuccess = m_xEmbeddedObject->getCurrentState() == EmbedStates::LOADED; + } + return bSuccess; +} + +Reference< XComponent > SAL_CALL ODocumentDefinition::open( ) +{ + return impl_openUI_nolck_throw( false ); +} + +Reference< XComponent > SAL_CALL ODocumentDefinition::openDesign( ) +{ + return impl_openUI_nolck_throw( true ); +} + +void SAL_CALL ODocumentDefinition::store( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + try + { + impl_store_throw(); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } +} + +sal_Bool SAL_CALL ODocumentDefinition::close( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bSuccess = false; + try + { + bSuccess = impl_close_throw(); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + throw WrappedTargetException( + OUString(), *this, ::cppu::getCaughtException() ); + } + return bSuccess; +} + +OUString SAL_CALL ODocumentDefinition::getHierarchicalName() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return impl_getHierarchicalName( false ); +} + +OUString SAL_CALL ODocumentDefinition::composeHierarchicalName( const OUString& i_rRelativeName ) +{ + return getHierarchicalName() + "/" + i_rRelativeName; +} + +void SAL_CALL ODocumentDefinition::rename( const OUString& _rNewName ) +{ + try + { + ::osl::ResettableMutexGuard aGuard(m_aMutex); + if ( _rNewName == m_pImpl->m_aProps.aTitle ) + return; + + // document definitions are organized in a hierarchical way, so reject names + // which contain a /, as this is reserved for hierarchy level separation + if ( _rNewName.indexOf( '/' ) != -1 ) + m_aErrorHelper.raiseException( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES, *this ); + + NameChangeNotifier aNameChangeAndNotify( *this, _rNewName, aGuard ); + m_pImpl->m_aProps.aTitle = _rNewName; + + if ( m_xEmbeddedObject.is() && m_xEmbeddedObject->getCurrentState() == EmbedStates::ACTIVE ) + updateDocumentTitle(); + } + catch(const PropertyVetoException&) + { + throw ElementExistException(_rNewName,*this); + } +} + +Reference< XStorage> ODocumentDefinition::getContainerStorage() const +{ + return m_pImpl->m_pDataSource + ? m_pImpl->m_pDataSource->getStorage( m_bForm ? ODatabaseModelImpl::E_FORM : ODatabaseModelImpl::E_REPORT ) + : Reference< XStorage>(); +} + +bool ODocumentDefinition::isModified() +{ + osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex); + bool bRet = false; + if ( m_xEmbeddedObject.is() ) + { + Reference xModel(getComponent(),UNO_QUERY); + bRet = xModel.is() && xModel->isModified(); + } + return bRet; +} + +bool ODocumentDefinition::prepareClose() +{ + if ( !m_xEmbeddedObject.is() ) + return true; + + try + { + // suspend the controller. Embedded objects are not allowed to raise + // own UI at their own discretion, instead, this has always to be triggered + // by the embedding component. Thus, we do the suspend call here. + // #i49370# + + Reference< util::XCloseable > xComponent( impl_getComponent_throw( false ) ); + if ( !xComponent.is() ) + return true; + + Reference< XModel > xModel( xComponent, UNO_QUERY ); + Reference< XController > xController; + if ( xModel.is() ) + xController = xModel->getCurrentController(); + + OSL_ENSURE( xController.is() || ( m_xEmbeddedObject->getCurrentState() < EmbedStates::ACTIVE ), + "ODocumentDefinition::prepareClose: no controller!" ); + if ( !xController.is() ) + // document has not yet been activated, i.e. has no UI, yet + return true; + + if (!xController->suspend(true)) + // controller vetoed the closing + return false; + + if ( isModified() ) + { + Reference< XFrame > xFrame( xController->getFrame() ); + Reference xTopWindow; + if ( xFrame.is() ) + { + xTopWindow = Reference(xFrame->getContainerWindow(), UNO_QUERY_THROW); + xTopWindow->toFront(); + } + if (!save(true, xTopWindow)) + { + // revert suspension + xController->suspend(false); + // saving failed or was cancelled + return false; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return true; +} + +void ODocumentDefinition::fillReportData( const Reference< XComponentContext >& _rContext, + const Reference< util::XCloseable >& _rxComponent, + const Reference< XConnection >& _rxActiveConnection ) +{ + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"TextDocument", uno::Any(_rxComponent)}, + {"ActiveConnection", uno::Any(_rxActiveConnection)} + })); + try + { + Reference< XJobExecutor > xExecutable( + _rContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.wizards.report.CallReportWizard", aArgs, _rContext), UNO_QUERY_THROW ); + xExecutable->trigger( "fill" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void ODocumentDefinition::updateDocumentTitle() +{ + OUString sName = m_pImpl->m_aProps.aTitle; + if ( m_pImpl->m_pDataSource ) + { + if ( sName.isEmpty() ) + { + if ( m_bForm ) + sName = DBA_RES( RID_STR_FORM ); + else + sName = DBA_RES( RID_STR_REPORT ); + Reference< XUntitledNumbers > xUntitledProvider(m_pImpl->m_pDataSource->getModel_noCreate(), UNO_QUERY ); + if ( xUntitledProvider.is() ) + sName += OUString::number( xUntitledProvider->leaseNumber(getComponent()) ); + } + + Reference< XTitle > xDatabaseDocumentModel(m_pImpl->m_pDataSource->getModel_noCreate(),uno::UNO_QUERY); + if ( xDatabaseDocumentModel.is() ) + sName = xDatabaseDocumentModel->getTitle() + " : " + sName; + } + Reference< XTitle> xTitle(getComponent(),UNO_QUERY); + if ( xTitle.is() ) + xTitle->setTitle(sName); +} + +void SAL_CALL ODocumentDefinition::queryClosing( const lang::EventObject&, sal_Bool ) +{ + try + { + if ( !close() ) + throw util::CloseVetoException(); + } + catch(const lang::WrappedTargetException&) + { + throw util::CloseVetoException(); + } +} + +void SAL_CALL ODocumentDefinition::notifyClosing( const lang::EventObject& /*Source*/ ) +{ +} + +void SAL_CALL ODocumentDefinition::disposing( const lang::EventObject& /*Source*/ ) +{ +} + +void ODocumentDefinition::firePropertyChange( sal_Int32 i_nHandle, const Any& i_rNewValue, const Any& i_rOldValue, + bool i_bVetoable, const NotifierAccess& ) +{ + fire( &i_nHandle, &i_rNewValue, &i_rOldValue, 1, i_bVetoable ); +} + +// NameChangeNotifier +NameChangeNotifier::NameChangeNotifier( ODocumentDefinition& i_rDocumentDefinition, const OUString& i_rNewName, + ::osl::ResettableMutexGuard& i_rClearForNotify ) + :m_rDocumentDefinition( i_rDocumentDefinition ) + ,m_aOldValue( Any( i_rDocumentDefinition.getCurrentName() ) ) + ,m_aNewValue( Any( i_rNewName ) ) + ,m_rClearForNotify( i_rClearForNotify ) +{ + impl_fireEvent_throw( true ); +} + +NameChangeNotifier::~NameChangeNotifier() +{ + impl_fireEvent_throw( false ); +} + +void NameChangeNotifier::impl_fireEvent_throw( const bool i_bVetoable ) +{ + m_rClearForNotify.clear(); + m_rDocumentDefinition.firePropertyChange( + PROPERTY_ID_NAME, m_aNewValue, m_aOldValue, i_bVetoable, ODocumentDefinition::NotifierAccess() ); + m_rClearForNotify.reset(); +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentdefinition.hxx b/dbaccess/source/core/dataaccess/documentdefinition.hxx new file mode 100644 index 000000000..5569276d4 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentdefinition.hxx @@ -0,0 +1,356 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace comphelper +{ + class NamedValueCollection; +} + +namespace dbaccess +{ + + class OInterceptor; + class OEmbeddedClientHelper; +// ODocumentDefinition - a database "document" which is simply a link to a real +// document + +typedef ::cppu::ImplHelper4 < css::embed::XComponentSupplier + , css::sdb::XSubDocument + , css::util::XCloseListener + , css::container::XHierarchicalName + > ODocumentDefinition_Base; + +class ODocumentDefinition + :public OContentHelper + ,public ::comphelper::OPropertyStateContainer + ,public ::comphelper::OPropertyArrayUsageHelper< ODocumentDefinition > + ,public ODocumentDefinition_Base +{ + css::uno::Reference< css::embed::XEmbeddedObject> m_xEmbeddedObject; + css::uno::Reference< css::embed::XStateChangeListener > m_xListener; + css::uno::Reference< css::sdbc::XConnection > m_xLastKnownConnection; + + rtl::Reference m_pInterceptor; + bool m_bForm; // if it is a form + bool m_bOpenInDesign; + bool m_bInExecute; + bool m_bRemoveListener; + rtl::Reference m_pClientHelper; + +protected: + virtual ~ODocumentDefinition() override; + +public: + + ODocumentDefinition( + const css::uno::Reference< css::uno::XInterface >& _rxContainer, + const css::uno::Reference< css::uno::XComponentContext >&, + const TContentPtr& _pImpl, + bool _bForm + ); + + void initialLoad( + const css::uno::Sequence< sal_Int8 >& i_rClassID, + const css::uno::Sequence< css::beans::PropertyValue >& i_rCreationArgs, + const css::uno::Reference< css::sdbc::XConnection >& i_rConnection + ); + + virtual css::uno::Sequence SAL_CALL getTypes() override; + virtual css::uno::Sequence SAL_CALL getImplementationId() override; + +// css::uno::XInterface + DECLARE_XINTERFACE( ) + +// css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // OPropertySetHelper + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& o_rValue, + sal_Int32 i_nHandle + ) const override; + + // XComponentSupplier + virtual css::uno::Reference< css::util::XCloseable > SAL_CALL getComponent( ) override; + + // XSubDocument + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL open( ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL openDesign( ) override; + virtual void SAL_CALL store( ) override; + virtual sal_Bool SAL_CALL close( ) override; + + // XHierarchicalName + virtual OUString SAL_CALL getHierarchicalName( ) override; + virtual OUString SAL_CALL composeHierarchicalName( const OUString& aRelativeName ) override; + +// OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + + // XCommandProcessor + virtual css::uno::Any SAL_CALL execute( const css::ucb::Command& aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override ; + + // XRename + virtual void SAL_CALL rename( const OUString& newName ) override; + + // XCloseListener + virtual void SAL_CALL queryClosing( const css::lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const css::lang::EventObject& Source ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + /** returns the forms/reports container storage, depending on m_bForm. Our own storage + inside this container storage is the one with the name as indicated by m_pImpl->m_aProps.sPersistentName. + */ + css::uno::Reference< css::embed::XStorage > + getContainerStorage() const; + + bool save(bool _bApprove, const css::uno::Reference& rDialogParent); + void saveAs(); + void closeObject(); + bool isModified(); + bool isNewReport() const { return !m_bForm && !m_pImpl->m_aProps.bAsTemplate; } + + static void fillReportData( + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + const css::uno::Reference< css::util::XCloseable >& _rxComponent, + const css::uno::Reference< css::sdbc::XConnection >& _rxActiveConnection + ); + + const css::uno::Reference< css::sdbc::XConnection >& + getConnection() const { return m_xLastKnownConnection; } + + /** prepares closing the document component + + The method suspends the controller associated with the document, and saves the document + if necessary. + + @return + if and only if the document component can be closed + */ + bool prepareClose(); + + static OUString GetDocumentServiceFromMediaType( + const OUString& _rMediaType, + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + css::uno::Sequence< sal_Int8 >& _rClassId + ); + static OUString GetDocumentServiceFromMediaType( + const css::uno::Reference< css::embed::XStorage >& _rxContainerStorage, + const OUString& _rEntityName, + const css::uno::Reference< css::uno::XComponentContext > & _rxContext, + css::uno::Sequence< sal_Int8 >& _rClassId + ); + + struct NotifierAccess { friend class NameChangeNotifier; private: NotifierAccess() { } }; + const OUString& getCurrentName() const { return m_pImpl->m_aProps.aTitle; } + void firePropertyChange( + sal_Int32 i_nHandle, + const css::uno::Any& i_rNewValue, + const css::uno::Any& i_rOldValue, + bool i_bVetoable, + const NotifierAccess& + ); + +private: + /** does necessary initializations after our embedded object has been switched to ACTIVE + */ + void impl_onActivateEmbeddedObject_nothrow( const bool i_bReactivated ); + + /** initializes a newly created view/controller of a form which is displaying our embedded object + + Has only to be called if the respective embedded object has been loaded for design (and + not for data entry) + + @param _rxController + the controller which belongs to the XModel of our (active) embedded object + */ + static void impl_initFormEditView( const css::uno::Reference< css::frame::XController >& _rxController ); + + /** removes the given frame from the desktop's frame collection + @throws css::uno::RuntimeException + */ + static void impl_removeFrameFromDesktop_throw( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::frame::XFrame >& _rxFrame + ); + + /** opens the UI for this sub document + */ + css::uno::Reference< css::lang::XComponent > + impl_openUI_nolck_throw( bool _bForEditing ); + + /** stores our document, if it's already loaded + */ + void impl_store_throw(); + + /** closes our document, if it's open + */ + bool impl_close_throw(); + + /** returns our component, creates it if necessary + */ + css::uno::Reference< css::util::XCloseable > + impl_getComponent_throw( const bool i_ForceCreate = true ); + + /** shows or hides our component + + The embedded object must exist, and be in state LOADED, at least. + */ + void impl_showOrHideComponent_throw( const bool i_bShow ); + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + + virtual void getPropertyDefaultByHandle( sal_Int32 _nHandle, css::uno::Any& _rDefault ) const override; + + // helper + virtual void SAL_CALL disposing() override; + + // OContentHelper overridables + virtual OUString determineContentType() const override; + + /** fills the load arguments + */ + css::uno::Sequence< css::beans::PropertyValue > + fillLoadArgs( + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const bool _bSuppressMacros, + const bool _bReadOnly, + const css::uno::Sequence< css::beans::PropertyValue >& i_rOpenCommandArguments, + css::uno::Sequence< css::beans::PropertyValue >& _out_rEmbeddedObjectDescriptor + ); + + /** splits the given arguments to an "open*" command into arguments for loading the document, and arguments to be + put into the EmbeddedObjectDescriptor + + Any values already present in o_rDocumentLoadArgs and o_rEmbeddedObjectDescriptor + will be overwritten by values from i_rOpenCommandArguments, if applicable, otherwise they will + be preserved. + + @param i_rOpenCommandArguments + the arguments passed to the "open*" command at the content + @param o_rDocumentLoadArgs + the arguments to be passed when actually loading the embedded document. + @param o_rEmbeddedObjectDescriptor + the EmbeddedObjectDescriptor to be passed when initializing the embedded object + */ + static void separateOpenCommandArguments( + const css::uno::Sequence< css::beans::PropertyValue >& i_rOpenCommandArguments, + ::comphelper::NamedValueCollection& o_rDocumentLoadArgs, + ::comphelper::NamedValueCollection& o_rEmbeddedObjectDescriptor + ); + + /** loads the EmbeddedObject if not already loaded + @param _aClassID + If set, it will be used to create the embedded object. + */ + void loadEmbeddedObject( + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const css::uno::Sequence< sal_Int8 >& _aClassID, + const css::uno::Sequence< css::beans::PropertyValue >& _rAdditionalArgs, + const bool _bSuppressMacros, + const bool _bReadOnly + ); + + /** loads the embedded object for preview. Macros will be suppressed, and the document will + be read-only. + */ + void loadEmbeddedObjectForPreview() + { + loadEmbeddedObject( + nullptr, + css::uno::Sequence< sal_Int8 >(), + css::uno::Sequence< css::beans::PropertyValue >(), + true, + true + ); + } + + /** searches for read-only flag in the args of the model and sets it to the given value, + if the value was not found, it will be appended. + @param _bReadOnly + If the document will be switched to readonly mode + */ + void updateDocumentTitle(); + + void registerProperties(); + + /** determines whether the document we represent supports embedded scripts and macros + */ + bool objectSupportsEmbeddedScripts() const; + + //- commands + + void onCommandGetDocumentProperties( css::uno::Any& _rProps ); + /// @throws css::uno::Exception + void onCommandInsert( const OUString& _sURL, const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ); + void onCommandPreview( css::uno::Any& _rImage ); + css::uno::Any + onCommandOpenSomething( + const css::uno::Any& _rArgument, + const bool _bActivate, + const css::uno::Reference< css::ucb::XCommandEnvironment >& _rxEnvironment + ); +private: + using ::cppu::OPropertySetHelper::getFastPropertyValue; +}; + +class NameChangeNotifier +{ +public: + NameChangeNotifier( + ODocumentDefinition& i_rDocumentDefinition, + const OUString& i_rNewName, + ::osl::ResettableMutexGuard& i_rClearForNotify + ); + ~NameChangeNotifier(); + +private: + ODocumentDefinition& m_rDocumentDefinition; + const css::uno::Any m_aOldValue; + const css::uno::Any m_aNewValue; + ::osl::ResettableMutexGuard& m_rClearForNotify; + + void impl_fireEvent_throw( const bool i_bVetoable ); +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventexecutor.cxx b/dbaccess/source/core/dataaccess/documenteventexecutor.cxx new file mode 100644 index 000000000..3d16ee6eb --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventexecutor.cxx @@ -0,0 +1,199 @@ +/* -*- 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 "documenteventexecutor.hxx" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::WeakReference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::document::XDocumentEventBroadcaster; + using ::com::sun::star::document::XEventsSupplier; + using ::com::sun::star::container::XNameAccess; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::util::URLTransformer; + using ::com::sun::star::util::XURLTransformer; + using ::com::sun::star::frame::XDispatchProvider; + using ::com::sun::star::frame::XDispatch; + using ::com::sun::star::util::URL; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::frame::XController; + using ::com::sun::star::document::DocumentEvent; + + using namespace ::com::sun::star; + + // DocumentEventExecutor_Data + struct DocumentEventExecutor_Data + { + WeakReference< XEventsSupplier > xDocument; + Reference< XURLTransformer > xURLTransformer; + + explicit DocumentEventExecutor_Data( const Reference< XEventsSupplier >& _rxDocument ) + :xDocument( _rxDocument ) + { + } + }; + + namespace + { + void lcl_dispatchScriptURL_throw( DocumentEventExecutor_Data const & _rDocExecData, + const OUString& _rScriptURL, const DocumentEvent& _rTrigger ) + { + Reference< XModel > xDocument( _rDocExecData.xDocument.get(), UNO_QUERY_THROW ); + + Reference< XController > xController( xDocument->getCurrentController() ); + Reference< XDispatchProvider > xDispProv; + if ( xController.is() ) + xDispProv.set( xController->getFrame(), UNO_QUERY ); + if ( !xDispProv.is() ) + { + OSL_FAIL( "lcl_dispatchScriptURL_throw: no controller/frame? How should I dispatch?" ); + return; + } + + URL aScriptURL; + aScriptURL.Complete = _rScriptURL; + if ( _rDocExecData.xURLTransformer.is() ) + _rDocExecData.xURLTransformer->parseStrict( aScriptURL ); + + // unfortunately, executing a script can trigger all kind of complex stuff, and unfortunately, not + // every component involved into this properly cares for thread safety. To be on the safe side, + // we lock the solar mutex here. + SolarMutexGuard aSolarGuard; + + Reference< XDispatch > xDispatch( xDispProv->queryDispatch( aScriptURL, OUString(), 0 ) ); + if ( !xDispatch.is() ) + { + OSL_FAIL( "lcl_dispatchScriptURL_throw: no dispatcher for the script URL!" ); + return; + } + + PropertyValue aEventParam; + aEventParam.Value <<= _rTrigger; + Sequence< PropertyValue > aDispatchArgs( &aEventParam, 1 ); + xDispatch->dispatch( aScriptURL, aDispatchArgs ); + } + } + + // DocumentEventExecutor + DocumentEventExecutor::DocumentEventExecutor( const Reference & _rContext, + const Reference< XEventsSupplier >& _rxDocument ) + :m_pData( new DocumentEventExecutor_Data( _rxDocument ) ) + { + Reference< XDocumentEventBroadcaster > xBroadcaster( _rxDocument, UNO_QUERY_THROW ); + + osl_atomic_increment( &m_refCount ); + { + xBroadcaster->addDocumentEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + + try + { + m_pData->xURLTransformer = URLTransformer::create(_rContext); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + DocumentEventExecutor::~DocumentEventExecutor() + { + } + + void SAL_CALL DocumentEventExecutor::documentEventOccured( const DocumentEvent& Event ) + { + Reference< XEventsSupplier > xEventsSupplier( m_pData->xDocument.get(), UNO_QUERY ); + if ( !xEventsSupplier.is() ) + { + OSL_FAIL( "DocumentEventExecutor::documentEventOccurred: no document anymore, but still being notified?" ); + return; + } + + Reference< XModel > xDocument( xEventsSupplier, UNO_QUERY_THROW ); + + try + { + Reference< XNameAccess > xDocEvents( xEventsSupplier->getEvents(), UNO_SET_THROW ); + if ( !xDocEvents->hasByName( Event.EventName ) ) + { + // this is worth an assertion: We are listener at the very same document which we just asked + // for its events. So when EventName is fired, why isn't it supported by xDocEvents? + OSL_FAIL( "DocumentEventExecutor::documentEventOccurred: an unsupported event is notified!" ); + return; + } + + const ::comphelper::NamedValueCollection aScriptDescriptor( xDocEvents->getByName( Event.EventName ) ); + + OUString sEventType; + bool bScriptAssigned = aScriptDescriptor.get_ensureType( "EventType", sEventType ); + + OUString sScript; + bScriptAssigned = bScriptAssigned && aScriptDescriptor.get_ensureType( "Script", sScript ); + + if ( !bScriptAssigned ) + // no script is assigned to this event + return; + + bool bDispatchScriptURL = ( sEventType == "Script" || sEventType == "Service" ); + bool bNonEmptyScript = !sScript.isEmpty(); + + OSL_ENSURE( bDispatchScriptURL && bNonEmptyScript, + "DocumentEventExecutor::documentEventOccurred: invalid/unsupported script descriptor" ); + + if ( bDispatchScriptURL && bNonEmptyScript ) + { + lcl_dispatchScriptURL_throw( *m_pData, sScript, Event ); + } + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void SAL_CALL DocumentEventExecutor::disposing( const lang::EventObject& /*_Source*/ ) + { + // not interested in + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventexecutor.hxx b/dbaccess/source/core/dataaccess/documenteventexecutor.hxx new file mode 100644 index 000000000..90dc9083c --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventexecutor.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace com::sun::star::uno { class XComponentContext; } + +namespace dbaccess +{ + + struct DocumentEventExecutor_Data; + // DocumentEventExecutor + typedef ::cppu::WeakImplHelper < css::document::XDocumentEventListener + > DocumentEventExecutor_Base; + class DocumentEventExecutor : public DocumentEventExecutor_Base + { + public: + DocumentEventExecutor( + const css::uno::Reference< css::uno::XComponentContext >& _rContext, + const css::uno::Reference< css::document::XEventsSupplier >& _rxDocument ); + + protected: + virtual ~DocumentEventExecutor() override; + + // css.document.XDocumentEventListener + virtual void SAL_CALL documentEventOccured( const css::document::DocumentEvent& Event ) override; + // css.lang.XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + private: + std::unique_ptr< DocumentEventExecutor_Data > m_pData; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventnotifier.cxx b/dbaccess/source/core/dataaccess/documenteventnotifier.cxx new file mode 100644 index 000000000..6a7088f95 --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventnotifier.cxx @@ -0,0 +1,290 @@ +/* -*- 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 "documenteventnotifier.hxx" + +#include + +#include +#include +#include +#include +#include + +namespace dbaccess +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Any; + using ::com::sun::star::frame::DoubleInitializationException; + using ::com::sun::star::document::XDocumentEventListener; + using ::com::sun::star::document::DocumentEvent; + using ::com::sun::star::frame::XController2; + + using namespace ::com::sun::star; + + // DocumentEventHolder + typedef ::comphelper::EventHolder< DocumentEvent > DocumentEventHolder; + + // DocumentEventNotifier_Impl + class DocumentEventNotifier_Impl : public ::comphelper::IEventProcessor + { + oslInterlockedCount m_refCount; + ::cppu::OWeakObject& m_rDocument; + ::osl::Mutex& m_rMutex; + bool m_bInitialized; + bool m_bDisposed; + std::shared_ptr<::comphelper::AsyncEventNotifierAutoJoin> m_pEventBroadcaster; + ::comphelper::OInterfaceContainerHelper3 m_aLegacyEventListeners; + ::comphelper::OInterfaceContainerHelper3 m_aDocumentEventListeners; + + public: + DocumentEventNotifier_Impl( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ) + :m_refCount( 0 ) + ,m_rDocument( _rBroadcasterDocument ) + ,m_rMutex( _rMutex ) + ,m_bInitialized( false ) + ,m_bDisposed( false ) + ,m_aLegacyEventListeners( _rMutex ) + ,m_aDocumentEventListeners( _rMutex ) + { + } + + // IEventProcessor + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + void addLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_aLegacyEventListeners.addInterface( Listener ); + } + + void removeLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_aLegacyEventListeners.removeInterface( Listener ); + } + + void addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_aDocumentEventListeners.addInterface( Listener ); + } + + void removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_aDocumentEventListeners.removeInterface( Listener ); + } + + void disposing(); + + void onDocumentInitialized(); + + void notifyDocumentEvent( const OUString& EventName, const Reference< XController2 >& ViewController, + const Any& Supplement ) + { + impl_notifyEvent_nothrow( DocumentEvent( + m_rDocument, EventName, ViewController, Supplement ) ); + } + + void notifyDocumentEventAsync( const OUString& EventName, const Reference< XController2 >& ViewController, + const Any& Supplement ) + { + impl_notifyEventAsync_nothrow( DocumentEvent( + m_rDocument, EventName, ViewController, Supplement ) ); + } + + protected: + virtual ~DocumentEventNotifier_Impl() + { + } + + // IEventProcessor + virtual void processEvent( const ::comphelper::AnyEvent& _rEvent ) override; + + private: + void impl_notifyEvent_nothrow( const DocumentEvent& _rEvent ); + void impl_notifyEventAsync_nothrow( const DocumentEvent& _rEvent ); + }; + + void SAL_CALL DocumentEventNotifier_Impl::acquire() noexcept + { + osl_atomic_increment( &m_refCount ); + } + + void SAL_CALL DocumentEventNotifier_Impl::release() noexcept + { + if ( 0 == osl_atomic_decrement( &m_refCount ) ) + delete this; + } + + void DocumentEventNotifier_Impl::disposing() + { + // SYNCHRONIZED -> + // cancel any pending asynchronous events + ::osl::ResettableMutexGuard aGuard( m_rMutex ); + if (m_pEventBroadcaster) + { + m_pEventBroadcaster->removeEventsForProcessor( this ); + m_pEventBroadcaster->terminate(); + } + + auto xEventBroadcaster = std::exchange(m_pEventBroadcaster, {}); + + lang::EventObject aEvent( m_rDocument ); + aGuard.clear(); + // <-- SYNCHRONIZED + + if (xEventBroadcaster) + { + comphelper::SolarMutex& rSolarMutex = Application::GetSolarMutex(); + // unblock threads blocked on that so we can join + sal_uInt32 nLockCount = (rSolarMutex.IsCurrentThread()) ? rSolarMutex.release(true) : 0; + xEventBroadcaster->join(); + if (nLockCount) + rSolarMutex.acquire(nLockCount); + xEventBroadcaster.reset(); + } + m_aLegacyEventListeners.disposeAndClear( aEvent ); + m_aDocumentEventListeners.disposeAndClear( aEvent ); + + // SYNCHRONIZED -> + aGuard.reset(); + m_bDisposed = true; + // <-- SYNCHRONIZED + } + + void DocumentEventNotifier_Impl::onDocumentInitialized() + { + if ( m_bInitialized ) + throw DoubleInitializationException(); + + m_bInitialized = true; + if (m_pEventBroadcaster) + { + // there are already pending asynchronous events + ::comphelper::AsyncEventNotifierAutoJoin::launch(m_pEventBroadcaster); + } + } + + void DocumentEventNotifier_Impl::impl_notifyEvent_nothrow( const DocumentEvent& _rEvent ) + { + OSL_PRECOND( m_bInitialized, + "DocumentEventNotifier_Impl::impl_notifyEvent_nothrow: only to be called when the document is already initialized!" ); + try + { + document::EventObject aLegacyEvent( _rEvent.Source, _rEvent.EventName ); + m_aLegacyEventListeners.notifyEach( &document::XEventListener::notifyEvent, aLegacyEvent ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + try + { + m_aDocumentEventListeners.notifyEach( &XDocumentEventListener::documentEventOccured, _rEvent ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + void DocumentEventNotifier_Impl::impl_notifyEventAsync_nothrow( const DocumentEvent& _rEvent ) + { + if (!m_pEventBroadcaster) + { + m_pEventBroadcaster = ::comphelper::AsyncEventNotifierAutoJoin + ::newAsyncEventNotifierAutoJoin("DocumentEventNotifier"); + if ( m_bInitialized ) + { + // start processing the events if and only if we (our document, respectively) are + // already initialized + ::comphelper::AsyncEventNotifierAutoJoin::launch(m_pEventBroadcaster); + } + } + m_pEventBroadcaster->addEvent( new DocumentEventHolder( _rEvent ), this ); + } + + void DocumentEventNotifier_Impl::processEvent( const ::comphelper::AnyEvent& _rEvent ) + { + // beware, this is called from the notification thread + { + ::osl::MutexGuard aGuard( m_rMutex ); + if ( m_bDisposed ) + return; + } + const DocumentEventHolder& rEventHolder = dynamic_cast< const DocumentEventHolder& >( _rEvent ); + impl_notifyEvent_nothrow( rEventHolder.getEventObject() ); + } + + // DocumentEventNotifier + DocumentEventNotifier::DocumentEventNotifier( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ) + :m_pImpl( new DocumentEventNotifier_Impl( _rBroadcasterDocument, _rMutex ) ) + { + } + + DocumentEventNotifier::~DocumentEventNotifier() + { + } + + void DocumentEventNotifier::disposing() + { + m_pImpl->disposing(); + } + + void DocumentEventNotifier::onDocumentInitialized() + { + m_pImpl->onDocumentInitialized(); + } + + void DocumentEventNotifier::addLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_pImpl->addLegacyEventListener( Listener ); + } + + void DocumentEventNotifier::removeLegacyEventListener( const Reference< document::XEventListener >& Listener ) + { + m_pImpl->removeLegacyEventListener( Listener ); + } + + void DocumentEventNotifier::addDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_pImpl->addDocumentEventListener( Listener ); + } + + void DocumentEventNotifier::removeDocumentEventListener( const Reference< XDocumentEventListener >& Listener ) + { + m_pImpl->removeDocumentEventListener( Listener ); + } + + void DocumentEventNotifier::notifyDocumentEvent( const OUString& EventName, + const Reference< XController2 >& ViewController, const Any& Supplement ) + { + m_pImpl->notifyDocumentEvent( EventName, ViewController, Supplement ); + } + + void DocumentEventNotifier::notifyDocumentEventAsync( const OUString& EventName, + const Reference< XController2 >& ViewController, const Any& Supplement ) + { + m_pImpl->notifyDocumentEventAsync( EventName, ViewController, Supplement ); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documenteventnotifier.hxx b/dbaccess/source/core/dataaccess/documenteventnotifier.hxx new file mode 100644 index 000000000..6f3bffc4c --- /dev/null +++ b/dbaccess/source/core/dataaccess/documenteventnotifier.hxx @@ -0,0 +1,128 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include + +#include + +namespace cppu +{ + class OWeakObject; +} + +namespace dbaccess +{ + + class DocumentEventNotifier_Impl; + // DocumentEventNotifier + class DocumentEventNotifier + { + public: + DocumentEventNotifier( ::cppu::OWeakObject& _rBroadcasterDocument, ::osl::Mutex& _rMutex ); + ~DocumentEventNotifier(); + + void addLegacyEventListener( const css::uno::Reference< css::document::XEventListener >& Listener ); + void removeLegacyEventListener( const css::uno::Reference< css::document::XEventListener >& Listener ); + void addDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ); + void removeDocumentEventListener( const css::uno::Reference< css::document::XDocumentEventListener >& Listener ); + + /** disposes the instance + @precond + the mutex is not locked + */ + void disposing(); + + /** tells the instance that its document is completely initialized now. + + Before you call this method, no notification will actually happen + + @precond + the mutex is locked + */ + void onDocumentInitialized(); + + /** notifies a document event described by the given parameters + + @precond + the mutex is not locked + @precond + ->onDocumentInitialized has been called + */ + void notifyDocumentEvent( + const OUString& EventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController, + const css::uno::Any& Supplement + ); + + /** notifies a document event, described by the given parameters, asynchronously + + Note that no event is actually notified before you called ->onDocumentInitialized. + + @precond + the mutex is locked + */ + void notifyDocumentEventAsync( + const OUString& EventName, + const css::uno::Reference< css::frame::XController2 >& ViewController, + const css::uno::Any& Supplement + ); + + /** notifies a document event to all registered listeners + + @precond + the mutex is not locked + @precond + ->onDocumentInitialized has been called + */ + void notifyDocumentEvent( + const char* _pAsciiEventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController = nullptr, + const css::uno::Any& _rSupplement = css::uno::Any() + ) + { + notifyDocumentEvent( OUString::createFromAscii( _pAsciiEventName ), _rxViewController, _rSupplement ); + } + + /** notifies a document event to all registered listeners, asynchronously + + Note that no event is actually notified before you called ->onDocumentInitialized. + + @precond + the mutex is locked + */ + void notifyDocumentEventAsync( + const char* _pAsciiEventName, + const css::uno::Reference< css::frame::XController2 >& _rxViewController = nullptr, + const css::uno::Any& _rSupplement = css::uno::Any() + ) + { + notifyDocumentEventAsync( OUString::createFromAscii( _pAsciiEventName ), _rxViewController, _rSupplement ); + } + + private: + ::rtl::Reference< DocumentEventNotifier_Impl > m_pImpl; + }; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/documentevents.cxx b/dbaccess/source/core/dataaccess/documentevents.cxx new file mode 100644 index 000000000..571ad2c6f --- /dev/null +++ b/dbaccess/source/core/dataaccess/documentevents.cxx @@ -0,0 +1,216 @@ +/* -*- 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 + +#include + +#include +#include +#include +#include + +namespace dbaccess +{ + + using ::com::sun::star::uno::Any; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Type; + + // DocumentEvents_Data + struct DocumentEvents_Data + { + ::cppu::OWeakObject& rParent; + ::osl::Mutex& rMutex; + DocumentEventsData& rEventsData; + + DocumentEvents_Data( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, DocumentEventsData& _rEventsData ) + :rParent( _rParent ) + ,rMutex( _rMutex ) + ,rEventsData( _rEventsData ) + { + } + DocumentEvents_Data(const DocumentEvents_Data&) = delete; + const DocumentEvents_Data& operator=(const DocumentEvents_Data&) = delete; + }; + + namespace { + + // helper + struct DocumentEventData + { + const char* pAsciiEventName; + bool bNeedsSyncNotify; + }; + + const DocumentEventData* lcl_getDocumentEventData() + { + static const DocumentEventData s_aData[] = { + { "OnCreate", true }, + { "OnLoadFinished", true }, + { "OnNew", false }, // compatibility, see https://bz.apache.org/ooo/show_bug.cgi?id=46484 + { "OnLoad", false }, // compatibility, see https://bz.apache.org/ooo/show_bug.cgi?id=46484 + { "OnSaveAs", true }, + { "OnSaveAsDone", false }, + { "OnSaveAsFailed", false }, + { "OnSave", true }, + { "OnSaveDone", false }, + { "OnSaveFailed", false }, + { "OnSaveTo", true }, + { "OnSaveToDone", false }, + { "OnSaveToFailed", false }, + { "OnPrepareUnload", true }, + { "OnUnload", true }, + { "OnFocus", false }, + { "OnUnfocus", false }, + { "OnModifyChanged", false }, + { "OnViewCreated", false }, + { "OnPrepareViewClosing", true }, + { "OnViewClosed", false }, + { "OnTitleChanged", false }, + { "OnSubComponentOpened", false }, + { "OnSubComponentClosed", false }, + { nullptr, false } + }; + return s_aData; + } + } + + // DocumentEvents + DocumentEvents::DocumentEvents( ::cppu::OWeakObject& _rParent, ::osl::Mutex& _rMutex, DocumentEventsData& _rEventsData ) + :m_pData( new DocumentEvents_Data( _rParent, _rMutex, _rEventsData ) ) + { + const DocumentEventData* pEventData = lcl_getDocumentEventData(); + while ( pEventData->pAsciiEventName ) + { + OUString sEventName = OUString::createFromAscii( pEventData->pAsciiEventName ); + DocumentEventsData::const_iterator existingPos = m_pData->rEventsData.find( sEventName ); + if ( existingPos == m_pData->rEventsData.end() ) + m_pData->rEventsData[ sEventName ] = Sequence< PropertyValue >(); + ++pEventData; + } + } + + DocumentEvents::~DocumentEvents() + { + } + + void SAL_CALL DocumentEvents::acquire() noexcept + { + m_pData->rParent.acquire(); + } + + void SAL_CALL DocumentEvents::release() noexcept + { + m_pData->rParent.release(); + } + + bool DocumentEvents::needsSynchronousNotification( std::u16string_view _rEventName ) + { + const DocumentEventData* pEventData = lcl_getDocumentEventData(); + while ( pEventData->pAsciiEventName ) + { + if ( o3tl::equalsAscii( _rEventName, pEventData->pAsciiEventName ) ) + return pEventData->bNeedsSyncNotify; + ++pEventData; + } + + // this is an unknown event ... assume async notification + return false; + } + + void SAL_CALL DocumentEvents::replaceByName( const OUString& Name, const Any& Element ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + DocumentEventsData::iterator elementPos = m_pData->rEventsData.find( Name ); + if ( elementPos == m_pData->rEventsData.end() ) + throw NoSuchElementException( Name, *this ); + + Sequence< PropertyValue > aEventDescriptor; + if ( Element.hasValue() && !( Element >>= aEventDescriptor ) ) + throw IllegalArgumentException( Element.getValueTypeName(), *this, 2 ); + + // Weird enough, the event assignment UI has (well: had) the idea of using an empty "EventType"/"Script" + // to indicate the event descriptor should be reset, instead of just passing an empty event descriptor. + ::comphelper::NamedValueCollection aCheck( aEventDescriptor ); + if ( aCheck.has( "EventType" ) ) + { + OUString sEventType = aCheck.getOrDefault( "EventType", OUString() ); + OSL_ENSURE( !sEventType.isEmpty(), "DocumentEvents::replaceByName: doing a reset via an empty EventType is weird!" ); + if ( sEventType.isEmpty() ) + aEventDescriptor.realloc( 0 ); + } + if ( aCheck.has( "Script" ) ) + { + OUString sScript = aCheck.getOrDefault( "Script", OUString() ); + OSL_ENSURE( !sScript.isEmpty(), "DocumentEvents::replaceByName: doing a reset via an empty Script is weird!" ); + if ( sScript.isEmpty() ) + aEventDescriptor.realloc( 0 ); + } + + elementPos->second = aEventDescriptor; + } + + Any SAL_CALL DocumentEvents::getByName( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + DocumentEventsData::const_iterator elementPos = m_pData->rEventsData.find( Name ); + if ( elementPos == m_pData->rEventsData.end() ) + throw NoSuchElementException( Name, *this ); + + Any aReturn; + const Sequence< PropertyValue >& rEventDesc( elementPos->second ); + if ( rEventDesc.hasElements() ) + aReturn <<= rEventDesc; + return aReturn; + } + + Sequence< OUString > SAL_CALL DocumentEvents::getElementNames( ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + return comphelper::mapKeysToSequence( m_pData->rEventsData ); + } + + sal_Bool SAL_CALL DocumentEvents::hasByName( const OUString& Name ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + + return m_pData->rEventsData.find( Name ) != m_pData->rEventsData.end(); + } + + Type SAL_CALL DocumentEvents::getElementType( ) + { + return ::cppu::UnoType< Sequence< PropertyValue > >::get(); + } + + sal_Bool SAL_CALL DocumentEvents::hasElements( ) + { + ::osl::MutexGuard aGuard( m_pData->rMutex ); + return !m_pData->rEventsData.empty(); + } + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/intercept.cxx b/dbaccess/source/core/dataaccess/intercept.cxx new file mode 100644 index 000000000..bef079d47 --- /dev/null +++ b/dbaccess/source/core/dataaccess/intercept.cxx @@ -0,0 +1,363 @@ +/* -*- 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 "intercept.hxx" + +#include + +#include + +namespace dbaccess +{ +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::container; +using namespace ::comphelper; +using namespace ::cppu; + +#define DISPATCH_SAVEAS 0 +#define DISPATCH_SAVE 1 +#define DISPATCH_CLOSEDOC 2 +#define DISPATCH_CLOSEWIN 3 +#define DISPATCH_CLOSEFRAME 4 +#define DISPATCH_RELOAD 5 +// the OSL_ENSURE in CTOR has to be changed too, when adding new defines + +void OInterceptor::dispose() +{ + EventObject aEvt( *this ); + + osl::MutexGuard aGuard(m_aMutex); + + if ( m_pStatCL ) + m_pStatCL->disposeAndClear( aEvt ); + + m_xSlaveDispatchProvider.clear(); + m_xMasterDispatchProvider.clear(); + + m_pContentHolder = nullptr; +} + + +OInterceptor::OInterceptor( ODocumentDefinition* _pContentHolder ) + :m_pContentHolder( _pContentHolder ) + ,m_aInterceptedURL{ /* DISPATCH_SAVEAS */ ".uno:SaveAs", + /* DISPATCH_SAVE */ ".uno:Save", + /* DISPATCH_CLOSEDOC */ ".uno:CloseDoc", + /* DISPATCH_CLOSEWIN */ ".uno:CloseWin", + /* DISPATCH_CLOSEFRAME */ ".uno:CloseFrame", + /* DISPATCH_RELOAD */ ".uno:Reload" } +{ + OSL_ENSURE(DISPATCH_RELOAD < m_aInterceptedURL.getLength(),"Illegal size."); +} + + +OInterceptor::~OInterceptor() +{ +} + +namespace { + +struct DispatchHelper +{ + URL aURL; + Sequence aArguments; +}; + +} + +//XDispatch +void SAL_CALL OInterceptor::dispatch( const URL& URL,const Sequence& Arguments ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !m_pContentHolder ) + return; + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_SAVE ] ) + { + m_pContentHolder->save(false, css::uno::Reference()); + return; + } + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_RELOAD ] ) + { + ODocumentDefinition::fillReportData( + m_pContentHolder->getContext(), + m_pContentHolder->getComponent(), + m_pContentHolder->getConnection() + ); + return; + } + + if( URL.Complete == m_aInterceptedURL[ DISPATCH_SAVEAS ] ) + { + if ( m_pContentHolder->isNewReport() ) + { + m_pContentHolder->saveAs(); + } + else if ( m_xSlaveDispatchProvider.is() ) + { + Sequence< PropertyValue > aNewArgs = Arguments; + sal_Int32 nInd = 0; + + while( nInd < aNewArgs.getLength() ) + { + if ( aNewArgs[nInd].Name == "SaveTo" ) + { + aNewArgs.getArray()[nInd].Value <<= true; + break; + } + nInd++; + } + + if ( nInd == aNewArgs.getLength() ) + { + aNewArgs.realloc( nInd + 1 ); + auto pNewArgs = aNewArgs.getArray(); + pNewArgs[nInd].Name = "SaveTo"; + pNewArgs[nInd].Value <<= true; + } + + Reference< XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch(URL, "_self", 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( URL, aNewArgs ); + } + return; + } + + if ( URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEDOC ] + || URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEWIN ] + || URL.Complete == m_aInterceptedURL[ DISPATCH_CLOSEFRAME ] + ) + { + DispatchHelper* pHelper = new DispatchHelper; + pHelper->aArguments = Arguments; + pHelper->aURL = URL; + Application::PostUserEvent( LINK( this, OInterceptor, OnDispatch ), pHelper ); + return; + } +} + +IMPL_LINK( OInterceptor, OnDispatch, void*, _pDispatcher, void ) +{ + std::unique_ptr pHelper( static_cast< DispatchHelper* >( _pDispatcher ) ); + try + { + if ( m_pContentHolder && m_pContentHolder->prepareClose() && m_xSlaveDispatchProvider.is() ) + { + Reference< XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch(pHelper->aURL, "_self", 0 ); + if ( xDispatch.is() ) + { + Reference< XInterface > xKeepContentHolderAlive( *m_pContentHolder ); + xDispatch->dispatch( pHelper->aURL,pHelper->aArguments); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL OInterceptor::addStatusListener( + const Reference< + XStatusListener >& Control, + const URL& URL ) +{ + if(!Control.is()) + return; + + if ( m_pContentHolder && URL.Complete == m_aInterceptedURL[DISPATCH_SAVEAS] ) + { // SaveAs + + if ( !m_pContentHolder->isNewReport() ) + { + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[DISPATCH_SAVEAS]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= OUString("($3)"); + Control->statusChanged(aStateEvent); + } + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + } + else if ( m_pContentHolder && URL.Complete == m_aInterceptedURL[DISPATCH_SAVE] ) + { // Save + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[DISPATCH_SAVE]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + + Control->statusChanged(aStateEvent); + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + } + else + { + sal_Int32 i = 2; + if(URL.Complete == m_aInterceptedURL[i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[i = DISPATCH_RELOAD] ) + { // Close and return + FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + Control->statusChanged(aStateEvent); + + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL.reset( new StatusListenerContainer(m_aMutex) ); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + } +} + + +void SAL_CALL OInterceptor::removeStatusListener( + const Reference< + XStatusListener >& Control, + const URL& URL ) +{ + if(!(Control.is() && m_pStatCL)) + return; + else + { + m_pStatCL->removeInterface(URL.Complete,Control); + return; + } +} + + +//XInterceptorInfo +Sequence< OUString > SAL_CALL OInterceptor::getInterceptedURLs( ) +{ + // now implemented as update + return m_aInterceptedURL; +} + + +// XDispatchProvider + +Reference< XDispatch > SAL_CALL OInterceptor::queryDispatch( const URL& URL,const OUString& TargetFrameName,sal_Int32 SearchFlags ) +{ + osl::MutexGuard aGuard(m_aMutex); + const OUString* pIter = m_aInterceptedURL.getConstArray(); + const OUString* pEnd = pIter + m_aInterceptedURL.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( URL.Complete == *pIter ) + return static_cast(this); + } + + if(m_xSlaveDispatchProvider.is()) + return m_xSlaveDispatchProvider->queryDispatch(URL,TargetFrameName,SearchFlags); + else + return Reference(); +} + +Sequence< Reference< XDispatch > > SAL_CALL OInterceptor::queryDispatches( const Sequence& Requests ) +{ + osl::MutexGuard aGuard(m_aMutex); + typedef Sequence> DispatchSeq; + DispatchSeq aRet = m_xSlaveDispatchProvider.is() ? + m_xSlaveDispatchProvider->queryDispatches(Requests) : + DispatchSeq(Requests.getLength()); + + auto aRetRange = asNonConstRange(aRet); + for(sal_Int32 i = 0; i < Requests.getLength(); ++i) + { + const OUString* pIter = m_aInterceptedURL.getConstArray(); + const OUString* pEnd = pIter + m_aInterceptedURL.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( Requests[i].FeatureURL.Complete == *pIter ) + { + aRetRange[i] = static_cast(this); + break; + } + } + } + + return aRet; +} + + +//XDispatchProviderInterceptor + +Reference< XDispatchProvider > SAL_CALL OInterceptor::getSlaveDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xSlaveDispatchProvider; +} + +void SAL_CALL +OInterceptor::setSlaveDispatchProvider( const Reference< XDispatchProvider >& NewDispatchProvider ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xSlaveDispatchProvider = NewDispatchProvider; +} + + +Reference< XDispatchProvider > SAL_CALL OInterceptor::getMasterDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xMasterDispatchProvider; +} + + +void SAL_CALL OInterceptor::setMasterDispatchProvider( + const Reference< XDispatchProvider >& NewSupplier ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xMasterDispatchProvider = NewSupplier; +} + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/intercept.hxx b/dbaccess/source/core/dataaccess/intercept.hxx new file mode 100644 index 000000000..7ce53752f --- /dev/null +++ b/dbaccess/source/core/dataaccess/intercept.hxx @@ -0,0 +1,113 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "documentdefinition.hxx" +#include + +namespace dbaccess +{ + + +class OInterceptor : public ::cppu::WeakImplHelper< css::frame::XDispatchProviderInterceptor, + css::frame::XInterceptorInfo, + css::frame::XDispatch > +{ + DECL_LINK( OnDispatch, void*, void ); +protected: + virtual ~OInterceptor() override; +public: + + explicit OInterceptor( ODocumentDefinition* _pContentHolder ); + + /// @throws css::uno::RuntimeException + void dispose(); + + //XDispatch + virtual void SAL_CALL + dispatch( + const css::util::URL& URL, + const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + + virtual void SAL_CALL + addStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + virtual void SAL_CALL + removeStatusListener( + const css::uno::Reference< css::frame::XStatusListener >& Control, + const css::util::URL& URL ) override; + + //XInterceptorInfo + virtual css::uno::Sequence< OUString > + SAL_CALL getInterceptedURLs( ) override; + + //XDispatchProvider ( inherited by XDispatchProviderInterceptor ) + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL + queryDispatch( + const css::util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) override; + + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL + queryDispatches( + const css::uno::Sequence< css::frame::DispatchDescriptor >& Requests ) override; + + //XDispatchProviderInterceptor + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getSlaveDispatchProvider( ) override; + + virtual void SAL_CALL + setSlaveDispatchProvider( + const css::uno::Reference< css::frame::XDispatchProvider >& NewDispatchProvider ) override; + + virtual css::uno::Reference< css::frame::XDispatchProvider > SAL_CALL + getMasterDispatchProvider( ) override; + + virtual void SAL_CALL + setMasterDispatchProvider( + const css::uno::Reference< css::frame::XDispatchProvider >& NewSupplier ) override; + +private: + + osl::Mutex m_aMutex; + + ODocumentDefinition* m_pContentHolder; + + css::uno::Reference< css::frame::XDispatchProvider > m_xSlaveDispatchProvider; + css::uno::Reference< css::frame::XDispatchProvider > m_xMasterDispatchProvider; + + css::uno::Sequence< OUString > m_aInterceptedURL; + + typedef comphelper::OMultiTypeInterfaceContainerHelperVar3 + StatusListenerContainer; + std::unique_ptr m_pStatCL; +}; + +} // namespace dbaccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx b/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx new file mode 100644 index 000000000..e0d32d569 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_datasupplier.cxx @@ -0,0 +1,330 @@ +/* -*- 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 + +#include + +#include "myucp_datasupplier.hxx" +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +// @@@ Adjust namespace name. +using namespace dbaccess; + +// @@@ Adjust namespace name. +namespace dbaccess +{ + +namespace { + +// struct ResultListEntry. +struct ResultListEntry +{ + OUString aId; + Reference< XContentIdentifier > xId; + ::rtl::Reference< OContentHelper > xContent; + Reference< XRow > xRow; + const ContentProperties& rData; + + explicit ResultListEntry(const ContentProperties& rEntry) : rData( rEntry ) {} +}; + +} + +// struct DataSupplier_Impl. +struct DataSupplier_Impl +{ + osl::Mutex m_aMutex; + std::vector< std::unique_ptr > m_aResults; + rtl::Reference< ODocumentContainer > m_xContent; + bool m_bCountFinal; + + explicit DataSupplier_Impl(const rtl::Reference< ODocumentContainer >& rContent) + : m_xContent(rContent) + , m_bCountFinal(false) + { + } +}; + +} + +// DataSupplier Implementation. + +DataSupplier::DataSupplier( const rtl::Reference< ODocumentContainer >& rContent ) +: m_pImpl( new DataSupplier_Impl( rContent ) ) +{ + +} + +DataSupplier::~DataSupplier() +{ + +} + +OUString DataSupplier::queryContentIdentifierString( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(nIndex) < m_pImpl->m_aResults.size() ) + { + OUString aId = m_pImpl->m_aResults[ nIndex ]->aId; + if ( !aId.isEmpty() ) + { + // Already cached. + return aId; + } + } + + if ( getResult( nIndex ) ) + { + OUString aId = m_pImpl->m_xContent->getIdentifier()->getContentIdentifier(); + + if ( !aId.isEmpty() ) + aId += "/"; + + aId += m_pImpl->m_aResults[ nIndex ]->rData.aTitle; + + m_pImpl->m_aResults[ nIndex ]->aId = aId; + return aId; + } + return OUString(); +} + +Reference< XContentIdentifier > +DataSupplier::queryContentIdentifier( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XContentIdentifier > xId = m_pImpl->m_aResults[ nIndex ]->xId; + if ( xId.is() ) + { + // Already cached. + return xId; + } + } + + OUString aId = queryContentIdentifierString( nIndex ); + if ( !aId.isEmpty() ) + { + Reference< XContentIdentifier > xId = new ::ucbhelper::ContentIdentifier( aId ); + m_pImpl->m_aResults[ nIndex ]->xId = xId; + return xId; + } + return Reference< XContentIdentifier >(); +} + +Reference< XContent > +DataSupplier::queryContent( sal_uInt32 _nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(_nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XContent > xContent = m_pImpl->m_aResults[ _nIndex ]->xContent; + if ( xContent.is() ) + { + // Already cached. + return xContent; + } + } + + Reference< XContentIdentifier > xId = queryContentIdentifier( _nIndex ); + if ( xId.is() ) + { + try + { + Reference< XContent > xContent; + OUString sName = xId->getContentIdentifier(); + sName = sName.copy(sName.lastIndexOf('/')+1); + + m_pImpl->m_aResults[ _nIndex ]->xContent = m_pImpl->m_xContent->getContent(sName); + + xContent = m_pImpl->m_aResults[ _nIndex ]->xContent.get(); + return xContent; + + } + catch ( IllegalIdentifierException& ) + { + } + } + return Reference< XContent >(); +} + +bool DataSupplier::getResult( sal_uInt32 nIndex ) +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(nIndex) < m_pImpl->m_aResults.size() ) + { + // Result already present. + return true; + } + + // Result not (yet) present. + + if ( m_pImpl->m_bCountFinal ) + return false; + + // Try to obtain result... + + sal_uInt32 nOldCount = m_pImpl->m_aResults.size(); + bool bFound = false; + sal_uInt32 nPos = nOldCount; + + // @@@ Obtain data and put it into result list... + Sequence< OUString> aSeq = m_pImpl->m_xContent->getElementNames(); + if ( nIndex < sal::static_int_cast< sal_uInt32 >( aSeq.getLength() ) ) + { + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(pIter = pIter + nPos;pIter != pEnd;++pIter,++nPos) + { + m_pImpl->m_aResults.emplace_back( + new ResultListEntry( m_pImpl->m_xContent->getContent(*pIter)->getContentProperties() ) ); + + if ( nPos == nIndex ) + { + // Result obtained. + bFound = true; + break; + } + } + } + + if ( !bFound ) + m_pImpl->m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + aGuard.clear(); + + if ( static_cast(nOldCount) < m_pImpl->m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_pImpl->m_aResults.size() ); + + if ( m_pImpl->m_bCountFinal ) + xResultSet->rowCountFinal(); + } + + return bFound; +} + +sal_uInt32 DataSupplier::totalCount() +{ + osl::ClearableGuard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( m_pImpl->m_bCountFinal ) + return m_pImpl->m_aResults.size(); + + sal_uInt32 nOldCount = m_pImpl->m_aResults.size(); + + // @@@ Obtain data and put it into result list... + Sequence< OUString> aSeq = m_pImpl->m_xContent->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + m_pImpl->m_aResults.emplace_back( + new ResultListEntry( m_pImpl->m_xContent->getContent(*pIter)->getContentProperties() ) ); + + m_pImpl->m_bCountFinal = true; + + rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet(); + if ( xResultSet.is() ) + { + // Callbacks follow! + aGuard.clear(); + + if ( static_cast(nOldCount) < m_pImpl->m_aResults.size() ) + xResultSet->rowCountChanged( + nOldCount, m_pImpl->m_aResults.size() ); + + xResultSet->rowCountFinal(); + } + + return m_pImpl->m_aResults.size(); +} + +sal_uInt32 DataSupplier::currentCount() +{ + return m_pImpl->m_aResults.size(); +} + +bool DataSupplier::isCountFinal() +{ + return m_pImpl->m_bCountFinal; +} + +Reference< XRow > +DataSupplier::queryPropertyValues( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(nIndex) < m_pImpl->m_aResults.size() ) + { + Reference< XRow > xRow = m_pImpl->m_aResults[ nIndex ]->xRow; + if ( xRow.is() ) + { + // Already cached. + return xRow; + } + } + + if ( getResult( nIndex ) ) + { + if ( !m_pImpl->m_aResults[ nIndex ]->xContent.is() ) + queryContent(nIndex); + + Reference< XRow > xRow = m_pImpl->m_aResults[ nIndex ]->xContent->getPropertyValues(getResultSet()->getProperties()); + m_pImpl->m_aResults[ nIndex ]->xRow = xRow; + return xRow; + } + + return Reference< XRow >(); +} + +void DataSupplier::releasePropertyValues( sal_uInt32 nIndex ) +{ + osl::Guard< osl::Mutex > aGuard( m_pImpl->m_aMutex ); + + if ( static_cast(nIndex) < m_pImpl->m_aResults.size() ) + m_pImpl->m_aResults[ nIndex ]->xRow.clear(); +} + +void DataSupplier::close() +{ +} + +void DataSupplier::validate() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx b/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx new file mode 100644 index 000000000..8083f5ef5 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_datasupplier.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include "documentcontainer.hxx" +#include + +namespace dbaccess +{ +struct DataSupplier_Impl; + +class DataSupplier : public ucbhelper::ResultSetDataSupplier +{ + std::unique_ptr m_pImpl; + +public: + explicit DataSupplier(const rtl::Reference& rxContent); + virtual ~DataSupplier() override; + + virtual OUString queryContentIdentifierString(sal_uInt32 nIndex) override; + virtual css::uno::Reference + queryContentIdentifier(sal_uInt32 nIndex) override; + virtual css::uno::Reference queryContent(sal_uInt32 nIndex) override; + + virtual bool getResult(sal_uInt32 nIndex) override; + + virtual sal_uInt32 totalCount() override; + virtual sal_uInt32 currentCount() override; + virtual bool isCountFinal() override; + + virtual css::uno::Reference queryPropertyValues(sal_uInt32 nIndex) override; + virtual void releasePropertyValues(sal_uInt32 nIndex) override; + + virtual void close() override; + + virtual void validate() override; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_resultset.cxx b/dbaccess/source/core/dataaccess/myucp_resultset.cxx new file mode 100644 index 000000000..67c1ad7c8 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_resultset.cxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +/************************************************************************** + TODO + ************************************************************************** + + - This implementation is not a dynamic result set!!! It only implements + the necessary interfaces, but never recognizes/notifies changes!!! + + *************************************************************************/ + +#include "myucp_datasupplier.hxx" +#include "myucp_resultset.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; + +using namespace dbaccess; + +// DynamicResultSet Implementation. +DynamicResultSet::DynamicResultSet( + const Reference< XComponentContext >& rxContext, + const rtl::Reference< ODocumentContainer >& rxContent, + const OpenCommandArgument2& rCommand, + const Reference< XCommandEnvironment >& rxEnv ) + :ResultSetImplHelper( rxContext, rCommand ) + ,m_xContent(rxContent) + ,m_xEnv( rxEnv ) +{ +} + +// Non-interface methods. +void DynamicResultSet::initStatic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContent ), + m_xEnv ); +} + +void DynamicResultSet::initDynamic() +{ + m_xResultSet1 + = new ::ucbhelper::ResultSet( m_xContext, + m_aCommand.Properties, + new DataSupplier( m_xContent ), + m_xEnv ); + m_xResultSet2 = m_xResultSet1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/core/dataaccess/myucp_resultset.hxx b/dbaccess/source/core/dataaccess/myucp_resultset.hxx new file mode 100644 index 000000000..ae269ffb8 --- /dev/null +++ b/dbaccess/source/core/dataaccess/myucp_resultset.hxx @@ -0,0 +1,49 @@ +/* -*- 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 . + */ + +#pragma once + +#include +#include +#include "documentcontainer.hxx" + + +// @@@ Adjust namespace name. +namespace dbaccess { + +class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper +{ + rtl::Reference< ODocumentContainer > m_xContent; + css::uno::Reference< css::ucb::XCommandEnvironment > m_xEnv; + +private: + virtual void initStatic() override; + virtual void initDynamic() override; + +public: + DynamicResultSet( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const rtl::Reference< ODocumentContainer >& rxContent, + const css::ucb::OpenCommandArgument2& rCommand, + const css::uno::Reference< css::ucb::XCommandEnvironment >& rxEnv ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3