diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /ucb/source/core | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream/4%7.4.7.tar.xz libreoffice-upstream/4%7.4.7.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ucb/source/core')
-rw-r--r-- | ucb/source/core/FileAccess.cxx | 694 | ||||
-rw-r--r-- | ucb/source/core/cmdenv.cxx | 117 | ||||
-rw-r--r-- | ucb/source/core/cmdenv.hxx | 65 | ||||
-rw-r--r-- | ucb/source/core/identify.cxx | 63 | ||||
-rw-r--r-- | ucb/source/core/identify.hxx | 40 | ||||
-rw-r--r-- | ucb/source/core/providermap.hxx | 63 | ||||
-rw-r--r-- | ucb/source/core/provprox.cxx | 353 | ||||
-rw-r--r-- | ucb/source/core/provprox.hxx | 126 | ||||
-rw-r--r-- | ucb/source/core/ucb.cxx | 887 | ||||
-rw-r--r-- | ucb/source/core/ucb.hxx | 158 | ||||
-rw-r--r-- | ucb/source/core/ucb1.component | 46 | ||||
-rw-r--r-- | ucb/source/core/ucbcmds.cxx | 1923 | ||||
-rw-r--r-- | ucb/source/core/ucbcmds.hxx | 36 | ||||
-rw-r--r-- | ucb/source/core/ucbprops.cxx | 268 | ||||
-rw-r--r-- | ucb/source/core/ucbprops.hxx | 56 | ||||
-rw-r--r-- | ucb/source/core/ucbstore.cxx | 2205 | ||||
-rw-r--r-- | ucb/source/core/ucbstore.hxx | 248 |
17 files changed, 7348 insertions, 0 deletions
diff --git a/ucb/source/core/FileAccess.cxx b/ucb/source/core/FileAccess.cxx new file mode 100644 index 000000000..24674ac07 --- /dev/null +++ b/ucb/source/core/FileAccess.cxx @@ -0,0 +1,694 @@ +/* -*- 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 <cppuhelper/exc_hlp.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <tools/urlobj.hxx> +#include <ucbhelper/content.hxx> +#include <unotools/streamwrap.hxx> +#include <tools/stream.hxx> + +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XActiveDataStreamer.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/ContentInfo.hpp> +#include <com/sun/star/ucb/ContentInfoAttribute.hpp> +#include <com/sun/star/ucb/InsertCommandArgument.hpp> +#include <com/sun/star/ucb/InteractiveIOException.hpp> +#include <com/sun/star/ucb/NameClash.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XSimpleFileAccess3.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> + +#include <vector> + +constexpr OUStringLiteral SERVICE_NAME = u"com.sun.star.ucb.SimpleFileAccess"; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::registry; +using namespace ::com::sun::star::container; + +using ::std::vector; + +namespace +{ + +// Implementation XSimpleFileAccess +typedef cppu::WeakImplHelper<XSimpleFileAccess3, css::lang::XServiceInfo> + FileAccessHelper; +class OCommandEnvironment; + +class OFileAccess : public FileAccessHelper +{ + Reference< XComponentContext > m_xContext; + rtl::Reference<OCommandEnvironment> mxEnvironment; + + /// @throws CommandAbortedException + /// @throws Exception + /// @throws RuntimeException + void transferImpl( const OUString& rSource, std::u16string_view rDest, bool bMoveData ); + /// @throws Exception + bool createNewFile( const OUString & rParentURL, + const OUString & rTitle, + const Reference< XInputStream >& data ); + +public: + explicit OFileAccess( const Reference< XComponentContext > & xContext ) + : m_xContext( xContext) {} + // Methods + virtual void SAL_CALL copy( const OUString& SourceURL, const OUString& DestURL ) override; + virtual void SAL_CALL move( const OUString& SourceURL, const OUString& DestURL ) override; + virtual void SAL_CALL kill( const OUString& FileURL ) override; + virtual sal_Bool SAL_CALL isFolder( const OUString& FileURL ) override; + virtual sal_Bool SAL_CALL isReadOnly( const OUString& FileURL ) override; + virtual void SAL_CALL setReadOnly( const OUString& FileURL, sal_Bool bReadOnly ) override; + virtual void SAL_CALL createFolder( const OUString& NewFolderURL ) override; + virtual sal_Int32 SAL_CALL getSize( const OUString& FileURL ) override; + virtual OUString SAL_CALL getContentType( const OUString& FileURL ) override; + virtual css::util::DateTime SAL_CALL getDateTimeModified( const OUString& FileURL ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getFolderContents( const OUString& FolderURL, sal_Bool bIncludeFolders ) override; + virtual sal_Bool SAL_CALL exists( const OUString& FileURL ) override; + virtual css::uno::Reference< css::io::XInputStream > SAL_CALL openFileRead( const OUString& FileURL ) override; + virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL openFileWrite( const OUString& FileURL ) override; + virtual css::uno::Reference< css::io::XStream > SAL_CALL openFileReadWrite( const OUString& FileURL ) override; + virtual void SAL_CALL setInteractionHandler( const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override; + virtual void SAL_CALL writeFile( const OUString& FileURL, const css::uno::Reference< css::io::XInputStream >& data ) override; + virtual sal_Bool SAL_CALL isHidden( const OUString& FileURL ) override; + virtual void SAL_CALL setHidden( const OUString& FileURL, sal_Bool bHidden ) override; + + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.ucb.SimpleFileAccess"; } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { return { SERVICE_NAME }; } +}; + +// Implementation XActiveDataSink + +class OActiveDataSink : public cppu::WeakImplHelper< XActiveDataSink > +{ + Reference< XInputStream > mxStream; + +public: + + // Methods + virtual void SAL_CALL setInputStream( const Reference< XInputStream >& aStream ) override; + virtual Reference< XInputStream > SAL_CALL getInputStream( ) override; +}; + +// Implementation XActiveDataStreamer + +class OActiveDataStreamer : public cppu::WeakImplHelper< XActiveDataStreamer > +{ + Reference< XStream > mxStream; + +public: + + // Methods + virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override; + virtual Reference< XStream > SAL_CALL getStream() override; +}; + +// Implementation XCommandEnvironment + +class OCommandEnvironment : public cppu::WeakImplHelper< XCommandEnvironment > +{ + Reference< XInteractionHandler > mxInteraction; + +public: + void setHandler( const Reference< XInteractionHandler >& xInteraction_ ) + { + mxInteraction = xInteraction_; + } + + // Methods + virtual Reference< XInteractionHandler > SAL_CALL getInteractionHandler() override; + virtual Reference< XProgressHandler > SAL_CALL getProgressHandler() override; +}; + +void OActiveDataSink::setInputStream( const Reference< XInputStream >& aStream ) +{ + mxStream = aStream; +} + +Reference< XInputStream > OActiveDataSink::getInputStream() +{ + return mxStream; +} + +void OActiveDataStreamer::setStream( const Reference< XStream >& aStream ) +{ + mxStream = aStream; +} + +Reference< XStream > OActiveDataStreamer::getStream() +{ + return mxStream; +} + +Reference< XInteractionHandler > OCommandEnvironment::getInteractionHandler() +{ + return mxInteraction; +} + +Reference< XProgressHandler > OCommandEnvironment::getProgressHandler() +{ + Reference< XProgressHandler > xRet; + return xRet; +} + +void OFileAccess::transferImpl( const OUString& rSource, + std::u16string_view rDest, + bool bMoveData ) +{ + // SfxContentHelper::Transfer_Impl + INetURLObject aSourceObj( rSource, INetProtocol::File ); + INetURLObject aDestObj( rDest, INetProtocol::File ); + OUString aName = aDestObj.getName( + INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + OUString aDestURL; + OUString aSourceURL = aSourceObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if ( aDestObj.removeSegment() ) + { + // hierarchical URL. + + aDestObj.setFinalSlash(); + aDestURL = aDestObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else + { + // non-hierarchical URL + + // #i29648# + + + if ( aDestObj.GetProtocol() == INetProtocol::VndSunStarExpand ) + { + // Hack: Expand destination URL using Macro Expander and try again + // with the hopefully hierarchical expanded URL... + + try + { + Reference< XMacroExpander > xExpander = theMacroExpander::get(m_xContext); + + aDestURL = xExpander->expandMacros( + aDestObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ) ); + } + catch ( Exception const & ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "OFileAccess::transferrImpl - Unable to obtain destination folder URL!", + static_cast< cppu::OWeakObject * >( this ), anyEx ); + } + + transferImpl( rSource, aDestURL, bMoveData ); + return; + } + + throw RuntimeException( + "OFileAccess::transferrImpl - Unable to obtain destination folder URL!", + static_cast< cppu::OWeakObject * >( this ) ); + + } + + ucbhelper::Content aDestPath( aDestURL, mxEnvironment, comphelper::getProcessComponentContext() ); + ucbhelper::Content aSrc ( aSourceURL, mxEnvironment, comphelper::getProcessComponentContext() ); + + try + { + aDestPath.transferContent(aSrc, + bMoveData + ? ucbhelper::InsertOperation::Move + : ucbhelper::InsertOperation::Copy, + aName, + css::ucb::NameClash::OVERWRITE); + } + catch ( css::ucb::CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + } +} + +void OFileAccess::copy( const OUString& SourceURL, const OUString& DestURL ) +{ + transferImpl( SourceURL, DestURL, false ); +} + +void OFileAccess::move( const OUString& SourceURL, const OUString& DestURL ) +{ + transferImpl( SourceURL, DestURL, true ); +} + +void OFileAccess::kill( const OUString& FileURL ) +{ + // SfxContentHelper::Kill + INetURLObject aDeleteObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aDeleteObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + try + { + aCnt.executeCommand( "delete", Any( true ) ); + } + catch ( css::ucb::CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + } +} + +sal_Bool OFileAccess::isFolder( const OUString& FileURL ) +{ + bool bRet = false; + try + { + INetURLObject aURLObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + bRet = aCnt.isFolder(); + } + catch (const Exception &) {} + return bRet; +} + +sal_Bool OFileAccess::isReadOnly( const OUString& FileURL ) +{ + INetURLObject aURLObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + Any aRetAny = aCnt.getPropertyValue("IsReadOnly"); + bool bRet = false; + aRetAny >>= bRet; + return bRet; +} + +void OFileAccess::setReadOnly( const OUString& FileURL, sal_Bool bReadOnly ) +{ + INetURLObject aURLObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + aCnt.setPropertyValue("IsReadOnly", Any(bReadOnly) ); +} + +void OFileAccess::createFolder( const OUString& NewFolderURL ) +{ + // Does the folder already exist? + if( NewFolderURL.isEmpty() || isFolder( NewFolderURL ) ) + return; + + // SfxContentHelper::MakeFolder + INetURLObject aURL( NewFolderURL, INetProtocol::File ); + OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + if ( !aTitle.isEmpty() ) + { + aURL.removeSegment(); + + // Does the base folder exist? Otherwise create it first + OUString aBaseFolderURLStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( !isFolder( aBaseFolderURLStr ) ) + { + createFolder( aBaseFolderURLStr ); + } + } + + ucbhelper::Content aCnt( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + + const Sequence< ContentInfo > aInfo = aCnt.queryCreatableContentsInfo(); + + for ( const ContentInfo & rCurr : aInfo ) + { + // Simply look for the first KIND_FOLDER... + if ( rCurr.Attributes & ContentInfoAttribute::KIND_FOLDER ) + { + // Make sure the only required bootstrap property is "Title", + const Sequence< Property > & rProps = rCurr.Properties; + if ( rProps.getLength() != 1 ) + continue; + + if ( rProps[ 0 ].Name != "Title" ) + continue; + + ucbhelper::Content aNew; + try + { + if ( !aCnt.insertNewContent( rCurr.Type, { "Title" }, { Any(aTitle) }, aNew ) ) + continue; + + // Success. We're done. + return; + } + catch ( css::ucb::CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + continue; + } + } + } +} + +sal_Int32 OFileAccess::getSize( const OUString& FileURL ) +{ + // SfxContentHelper::GetSize + sal_Int32 nSize = 0; + sal_Int64 nTemp = 0; + INetURLObject aObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + aCnt.getPropertyValue( "Size" ) >>= nTemp; + nSize = static_cast<sal_Int32>(nTemp); + return nSize; +} + +OUString OFileAccess::getContentType( const OUString& FileURL ) +{ + INetURLObject aObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + + Reference< XContent > xContent = aCnt.get(); + OUString aTypeStr = xContent->getContentType(); + return aTypeStr; +} + +css::util::DateTime OFileAccess::getDateTimeModified( const OUString& FileURL ) +{ + INetURLObject aFileObj( FileURL, INetProtocol::File ); + css::util::DateTime aDateTime; + + Reference< XCommandEnvironment > aCmdEnv; + ucbhelper::Content aYoung( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext() ); + aYoung.getPropertyValue("DateModified") >>= aDateTime; + return aDateTime; +} + +Sequence< OUString > OFileAccess::getFolderContents( const OUString& FolderURL, sal_Bool bIncludeFolders ) +{ + // SfxContentHelper::GetFolderContents + + std::vector<OUString> aFiles; + INetURLObject aFolderObj( FolderURL, INetProtocol::File ); + + ucbhelper::Content aCnt( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + Reference< XResultSet > xResultSet; + + ucbhelper::ResultSetInclude eInclude = bIncludeFolders ? ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS : ucbhelper::INCLUDE_DOCUMENTS_ONLY; + + try + { + xResultSet = aCnt.createCursor( {}, eInclude ); + } + catch ( css::ucb::CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + } + + if ( xResultSet.is() ) + { + Reference< css::ucb::XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); + + while ( xResultSet->next() ) + { + OUString aId = xContentAccess->queryContentIdentifierString(); + INetURLObject aURL( aId, INetProtocol::File ); + aFiles.push_back( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + } + } + + return comphelper::containerToSequence(aFiles); +} + +sal_Bool OFileAccess::exists( const OUString& FileURL ) +{ + bool bRet = false; + try + { + bRet = isFolder( FileURL ); + if( !bRet ) + { + Reference< XInputStream > xStream = openFileRead( FileURL ); + bRet = xStream.is(); + if( bRet ) + xStream->closeInput(); + } + } + catch (const Exception &) {} + return bRet; +} + +Reference< XInputStream > OFileAccess::openFileRead( const OUString& FileURL ) +{ + Reference< XInputStream > xRet; + INetURLObject aObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + + Reference<XActiveDataSink> xSink = new OActiveDataSink; + + try + { + bool bRet = aCnt.openStream( xSink ); + if( bRet ) + xRet = xSink->getInputStream(); + } + catch ( css::ucb::CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + } + + return xRet; +} + +Reference< XOutputStream > OFileAccess::openFileWrite( const OUString& FileURL ) +{ + Reference< XOutputStream > xRet; + Reference< XStream > xStream = OFileAccess::openFileReadWrite( FileURL ); + if( xStream.is() ) + xRet = xStream->getOutputStream(); + return xRet; +} + +Reference< XStream > OFileAccess::openFileReadWrite( const OUString& FileURL ) +{ + Reference<XActiveDataStreamer> xSink = new OActiveDataStreamer; + + OpenCommandArgument2 aArg; + aArg.Mode = OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = Sequence< Property >( 0 ); // unused + + Any aCmdArg; + aCmdArg <<= aArg; + + INetURLObject aFileObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aFileObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + + // Be silent... + Reference< XInteractionHandler > xIH; + if ( mxEnvironment.is() ) + { + xIH = mxEnvironment->getInteractionHandler(); + mxEnvironment->setHandler( nullptr ); + } + + try + { + aCnt.executeCommand( "open", aCmdArg ); + } + catch ( InteractiveIOException const & e ) + { + if ( xIH.is() && mxEnvironment.is() ) + mxEnvironment->setHandler( xIH ); + + if ( e.Code == IOErrorCode_NOT_EXISTING ) + { + // Create file... + SvMemoryStream aStream(0,0); + rtl::Reference<::utl::OInputStreamWrapper> pInput = new ::utl::OInputStreamWrapper( aStream ); + InsertCommandArgument aInsertArg; + aInsertArg.Data = pInput; + aInsertArg.ReplaceExisting = false; + + aCmdArg <<= aInsertArg; + aCnt.executeCommand( "insert", aCmdArg ); + + // Retry... + return openFileReadWrite( FileURL ); + } + + throw; + } + + if ( xIH.is() && mxEnvironment.is() ) + mxEnvironment->setHandler( xIH ); + + Reference< XStream > xRet = xSink->getStream(); + return xRet; +} + +void OFileAccess::setInteractionHandler( const Reference< XInteractionHandler >& Handler ) +{ + if( !mxEnvironment.is() ) + { + mxEnvironment = new OCommandEnvironment; + } + mxEnvironment->setHandler( Handler ); +} + +bool OFileAccess::createNewFile( const OUString & rParentURL, + const OUString & rTitle, + const Reference< XInputStream >& data ) +{ + ucbhelper::Content aParentCnt( rParentURL, mxEnvironment, comphelper::getProcessComponentContext() ); + + const Sequence< ContentInfo > aInfo = aParentCnt.queryCreatableContentsInfo(); + + for ( const ContentInfo & rCurr : aInfo ) + { + if ( ( rCurr.Attributes + & ContentInfoAttribute::KIND_DOCUMENT ) && + ( rCurr.Attributes + & ContentInfoAttribute::INSERT_WITH_INPUTSTREAM ) ) + { + // Make sure the only required bootstrap property is + // "Title", + const Sequence< Property > & rProps = rCurr.Properties; + if ( rProps.getLength() != 1 ) + continue; + + if ( rProps[ 0 ].Name != "Title" ) + continue; + + try + { + ucbhelper::Content aNew; + if ( aParentCnt.insertNewContent( + rCurr.Type, { "Title" }, { Any(rTitle) }, data, aNew ) ) + return true; // success. + else + continue; + } + catch ( CommandFailedException const & ) + { + // Interaction Handler already handled the + // error that has occurred... + continue; + } + } + } + + return false; +} + +void SAL_CALL OFileAccess::writeFile( const OUString& FileURL, + const Reference< XInputStream >& data ) +{ + INetURLObject aURL( FileURL, INetProtocol::File ); + try + { + ucbhelper::Content aCnt( + aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, + comphelper::getProcessComponentContext() ); + + try + { + aCnt.writeStream( data, true /* bReplaceExisting */ ); + } + catch ( CommandFailedException const & ) + { + // Interaction Handler already handled the error that has occurred... + } + } + catch ( ContentCreationException const & e ) + { + // Most probably file does not exist. Try to create. + if ( e.eError == ContentCreationError_CONTENT_CREATION_FAILED ) + { + INetURLObject aParentURLObj( aURL ); + if ( aParentURLObj.removeSegment() ) + { + OUString aParentURL + = aParentURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + // ensure all parent folders exist. + createFolder( aParentURL ); + + // create the new file... + OUString aTitle + = aURL.getName( INetURLObject::LAST_SEGMENT, + true, + INetURLObject::DecodeMechanism::WithCharset ); + if ( createNewFile( aParentURL, aTitle, data ) ) + { + // success + return; + } + } + } + + throw; + } +} + +sal_Bool OFileAccess::isHidden( const OUString& FileURL ) +{ + INetURLObject aURLObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + Any aRetAny = aCnt.getPropertyValue("IsHidden"); + bool bRet = false; + aRetAny >>= bRet; + return bRet; +} + +void OFileAccess::setHidden( const OUString& FileURL, sal_Bool bHidden ) +{ + INetURLObject aURLObj( FileURL, INetProtocol::File ); + ucbhelper::Content aCnt( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), mxEnvironment, comphelper::getProcessComponentContext() ); + aCnt.setPropertyValue("IsHidden", Any(bHidden) ); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_OFileAccess_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new OFileAccess(context)); +} + +}; // namespace end + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/cmdenv.cxx b/ucb/source/core/cmdenv.cxx new file mode 100644 index 000000000..9189f0e0f --- /dev/null +++ b/ucb/source/core/cmdenv.cxx @@ -0,0 +1,117 @@ +/* -*- 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 <cppuhelper/factory.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> + +#include "cmdenv.hxx" + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ +using namespace com::sun::star; +using namespace ucb_cmdenv; + +// UcbCommandEnvironment Implementation. + + +UcbCommandEnvironment::UcbCommandEnvironment() +{ +} + + +// virtual +UcbCommandEnvironment::~UcbCommandEnvironment() +{ +} + +// XInitialization methods. + + +// virtual +void SAL_CALL UcbCommandEnvironment::initialize( + const uno::Sequence< uno::Any >& aArguments ) +{ + if ( ( aArguments.getLength() < 2 ) || + !( aArguments[ 0 ] >>= m_xIH ) || + !( aArguments[ 1 ] >>= m_xPH )) + throw lang::IllegalArgumentException(); +} + + +// XServiceInfo methods. + + +// virtual +OUString SAL_CALL UcbCommandEnvironment::getImplementationName() +{ + return "com.sun.star.comp.ucb.CommandEnvironment"; +} + + +// virtual +sal_Bool SAL_CALL +UcbCommandEnvironment::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +// virtual +uno::Sequence< OUString > SAL_CALL +UcbCommandEnvironment::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.CommandEnvironment" }; +} + + +// XCommandInfo methods. + + +// virtual +uno::Reference< task::XInteractionHandler > SAL_CALL +UcbCommandEnvironment::getInteractionHandler() +{ + return m_xIH; +} + + +// virtual +uno::Reference< ucb::XProgressHandler > SAL_CALL +UcbCommandEnvironment::getProgressHandler() +{ + return m_xPH; +} + + +// Service factory implementation. + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UcbCommandEnvironment_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UcbCommandEnvironment()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/cmdenv.hxx b/ucb/source/core/cmdenv.hxx new file mode 100644 index 000000000..0cc4b46f4 --- /dev/null +++ b/ucb/source/core/cmdenv.hxx @@ -0,0 +1,65 @@ +/* -*- 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 <comphelper/compbase.hxx> + +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> + +namespace ucb_cmdenv { + +using UcbCommandEnvironment_Base = comphelper::WeakComponentImplHelper< css::lang::XInitialization, + css::lang::XServiceInfo, + css::ucb::XCommandEnvironment >; + +class UcbCommandEnvironment : public UcbCommandEnvironment_Base +{ + css::uno::Reference< css::task::XInteractionHandler > m_xIH; + css::uno::Reference< css::ucb::XProgressHandler > m_xPH; + +public: + explicit UcbCommandEnvironment(); + virtual ~UcbCommandEnvironment() override; + + // XInitialization + virtual void SAL_CALL + initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // 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; + + // XCommandEnvironment + virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL + getInteractionHandler() override; + virtual css::uno::Reference< css::ucb::XProgressHandler > SAL_CALL + getProgressHandler() override; +}; + +} // namespace ucb_cmdenv + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/identify.cxx b/ucb/source/core/identify.cxx new file mode 100644 index 000000000..41f5db735 --- /dev/null +++ b/ucb/source/core/identify.cxx @@ -0,0 +1,63 @@ +/* -*- 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 + ************************************************************************** + + *************************************************************************/ + +#include "identify.hxx" + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; + +// ContentIdentifier Implementation. +ContentIdentifier::ContentIdentifier(const OUString& ContentId) + : m_aContentId(ContentId) +{ +} + +// virtual +ContentIdentifier::~ContentIdentifier() {} + +// XContentIdentifier methods. +// virtual +OUString SAL_CALL ContentIdentifier::getContentIdentifier() { return m_aContentId; } + +// virtual +OUString SAL_CALL ContentIdentifier::getContentProviderScheme() +{ + if (m_aProviderScheme.isEmpty() && !m_aContentId.isEmpty()) + { + // The content provider scheme is the part before the first ':' + // within the content id. + sal_Int32 nPos = m_aContentId.indexOf(':'); + if (nPos != -1) + { + OUString aScheme(m_aContentId.copy(0, nPos)); + m_aProviderScheme = aScheme.toAsciiLowerCase(); + } + } + + return m_aProviderScheme; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/identify.hxx b/ucb/source/core/identify.hxx new file mode 100644 index 000000000..357fef39a --- /dev/null +++ b/ucb/source/core/identify.hxx @@ -0,0 +1,40 @@ +/* -*- 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 <com/sun/star/ucb/XContentIdentifier.hpp> +#include <cppuhelper/implbase.hxx> + +class ContentIdentifier : public cppu::WeakImplHelper<css::ucb::XContentIdentifier> +{ +public: + explicit ContentIdentifier(const OUString& ContentId); + virtual ~ContentIdentifier() override; + + // XContentIdentifier + virtual OUString SAL_CALL getContentIdentifier() override; + virtual OUString SAL_CALL getContentProviderScheme() override; + +private: + OUString m_aContentId; + OUString m_aProviderScheme; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/providermap.hxx b/ucb/source/core/providermap.hxx new file mode 100644 index 000000000..1d4c65aa5 --- /dev/null +++ b/ucb/source/core/providermap.hxx @@ -0,0 +1,63 @@ +/* -*- 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 <deque> +#include <com/sun/star/uno/Reference.h> +#include <regexpmap.hxx> + +namespace com::sun::star::ucb { + class XContentProvider; +} + + +class ProviderListEntry_Impl +{ + css::uno::Reference< + css::ucb::XContentProvider > m_xProvider; + mutable css::uno::Reference< + css::ucb::XContentProvider > m_xResolvedProvider; + +private: + css::uno::Reference< css::ucb::XContentProvider > const & resolveProvider() const; + +public: + explicit ProviderListEntry_Impl( + const css::uno::Reference< css::ucb::XContentProvider >& xProvider ) + : m_xProvider( xProvider ) {} + + const css::uno::Reference< css::ucb::XContentProvider >& getProvider() const + { return m_xProvider; } + inline css::uno::Reference< css::ucb::XContentProvider > const & getResolvedProvider() const; +}; + +inline css::uno::Reference< css::ucb::XContentProvider > const & +ProviderListEntry_Impl::getResolvedProvider() const +{ + return m_xResolvedProvider.is() ? m_xResolvedProvider : resolveProvider(); +} + + +typedef std::deque< ProviderListEntry_Impl > ProviderList_Impl; + + +typedef ucb_impl::RegexpMap< ProviderList_Impl > ProviderMap_Impl; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/provprox.cxx b/ucb/source/core/provprox.cxx new file mode 100644 index 000000000..7af7ba28a --- /dev/null +++ b/ucb/source/core/provprox.cxx @@ -0,0 +1,353 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/thread.h> +#include <rtl/strbuf.hxx> +#include <tools/diagnose_ex.h> +#include "provprox.hxx" +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <ucbhelper/macros.hxx> + +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; + +// UcbContentProviderProxyFactory Implementation. + + +UcbContentProviderProxyFactory::UcbContentProviderProxyFactory( + const Reference< XComponentContext >& rxContext ) +: m_xContext( rxContext ) +{ +} + + +// virtual +UcbContentProviderProxyFactory::~UcbContentProviderProxyFactory() +{ +} + +// XServiceInfo methods. + +OUString SAL_CALL UcbContentProviderProxyFactory::getImplementationName() +{ + return "com.sun.star.comp.ucb.UcbContentProviderProxyFactory"; +} +sal_Bool SAL_CALL UcbContentProviderProxyFactory::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} +css::uno::Sequence< OUString > SAL_CALL UcbContentProviderProxyFactory::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.ContentProviderProxyFactory" }; +} + +// Service factory implementation. + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UcbContentProviderProxyFactory_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UcbContentProviderProxyFactory(context)); +} + + +// XContentProviderFactory methods. + + +// virtual +Reference< XContentProvider > SAL_CALL +UcbContentProviderProxyFactory::createContentProvider( + const OUString& Service ) +{ + return Reference< XContentProvider >( + new UcbContentProviderProxy( m_xContext, Service ) ); +} + + +// UcbContentProviderProxy Implementation. + + +UcbContentProviderProxy::UcbContentProviderProxy( + const Reference< XComponentContext >& rxContext, + const OUString& Service ) +: m_aService( Service ), + m_bReplace( false ), + m_bRegister( false ), + m_xContext( rxContext ) +{ +} + + +// virtual +UcbContentProviderProxy::~UcbContentProviderProxy() +{ +} + + +// XInterface methods. +void SAL_CALL UcbContentProviderProxy::acquire() + noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL UcbContentProviderProxy::release() + noexcept +{ + OWeakObject::release(); +} + +// virtual +Any SAL_CALL +UcbContentProviderProxy::queryInterface( const Type & rType ) +{ + Any aRet = cppu::queryInterface( rType, + static_cast< XTypeProvider * >( this ), + static_cast< XServiceInfo * >( this ), + static_cast< XContentProvider * >( this ), + static_cast< XParameterizedContentProvider * >( this ), + static_cast< XContentProviderSupplier * >( this ) ); + + if ( !aRet.hasValue() ) + aRet = OWeakObject::queryInterface( rType ); + + if ( !aRet.hasValue() ) + { + // Get original provider and forward the call... + Reference< XContentProvider > xProvider = getContentProvider(); + if ( xProvider.is() ) + aRet = xProvider->queryInterface( rType ); + } + + return aRet; +} + + +// XTypeProvider methods. + + +XTYPEPROVIDER_COMMON_IMPL( UcbContentProviderProxy ); + + +Sequence< Type > SAL_CALL UcbContentProviderProxy::getTypes() +{ + // Get original provider and forward the call... + Reference< XTypeProvider > xProvider( getContentProvider(), UNO_QUERY ); + if ( xProvider.is() ) + { + return xProvider->getTypes(); + } + else + { + static cppu::OTypeCollection s_aCollection( + CPPU_TYPE_REF( XTypeProvider ), + CPPU_TYPE_REF( XServiceInfo ), + CPPU_TYPE_REF( XContentProvider ), + CPPU_TYPE_REF( XParameterizedContentProvider ), + CPPU_TYPE_REF( XContentProviderSupplier ) ); + return s_aCollection.getTypes(); + } +} + + +// XServiceInfo methods. + +OUString SAL_CALL UcbContentProviderProxy::getImplementationName() +{ + return "com.sun.star.comp.ucb.UcbContentProviderProxy"; +} + +sal_Bool SAL_CALL UcbContentProviderProxy::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +css::uno::Sequence< OUString > SAL_CALL UcbContentProviderProxy::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.ContentProviderProxy" }; +} + + +// XContentProvider methods. + + +// virtual +Reference< XContent > SAL_CALL UcbContentProviderProxy::queryContent( + const Reference< XContentIdentifier >& Identifier ) +{ + // Get original provider and forward the call... + + Reference< XContentProvider > xProvider = getContentProvider(); + if ( xProvider.is() ) + return xProvider->queryContent( Identifier ); + + return Reference< XContent >(); +} + + +// virtual +sal_Int32 SAL_CALL UcbContentProviderProxy::compareContentIds( + const Reference< XContentIdentifier >& Id1, + const Reference< XContentIdentifier >& Id2 ) +{ + // Get original provider and forward the call... + + Reference< XContentProvider > xProvider = getContentProvider(); + if ( xProvider.is() ) + return xProvider->compareContentIds( Id1, Id2 ); + + // OSL_FAIL( // "UcbContentProviderProxy::compareContentIds - No provider!" ); + + // @@@ What else? + return 0; +} + + +// XParameterizedContentProvider methods. + + +// virtual +Reference< XContentProvider > SAL_CALL +UcbContentProviderProxy::registerInstance( const OUString& Template, + const OUString& Arguments, + sal_Bool ReplaceExisting ) +{ + // Just remember that this method was called ( and the params ). + + std::scoped_lock aGuard( m_aMutex ); + + if ( !m_bRegister ) + { +// m_xTargetProvider = 0; + m_aTemplate = Template; + m_aArguments = Arguments; + m_bReplace = ReplaceExisting; + + m_bRegister = true; + } + return this; +} + + +// virtual +Reference< XContentProvider > SAL_CALL +UcbContentProviderProxy::deregisterInstance( const OUString& Template, + const OUString& Arguments ) +{ + std::scoped_lock aGuard( m_aMutex ); + + // registerInstance called at proxy and at original? + if ( m_bRegister && m_xTargetProvider.is() ) + { + m_bRegister = false; + m_xTargetProvider = nullptr; + + Reference< XParameterizedContentProvider > + xParamProvider( m_xProvider, UNO_QUERY ); + if ( xParamProvider.is() ) + { + try + { + xParamProvider->deregisterInstance( Template, Arguments ); + } + catch ( IllegalIdentifierException const & ) + { + OSL_FAIL( "UcbContentProviderProxy::deregisterInstance - " + "Caught IllegalIdentifierException!" ); + } + } + } + + return this; +} + + +// XContentProviderSupplier methods. + + +// virtual +Reference< XContentProvider > SAL_CALL +UcbContentProviderProxy::getContentProvider() +{ + std::scoped_lock aGuard( m_aMutex ); + if ( !m_xProvider.is() ) + { + try + { + m_xProvider.set( m_xContext->getServiceManager()->createInstanceWithContext( m_aService,m_xContext ), UNO_QUERY ); + if ( m_aArguments == "NoConfig" ) + { + Reference<XInitialization> xInit(m_xProvider,UNO_QUERY); + if(xInit.is()) { + xInit->initialize({ Any(m_aArguments) }); + } + } + } + catch ( RuntimeException const & ) + { + throw; + } + catch ( Exception const & ) + { + TOOLS_INFO_EXCEPTION( "ucb.core", "Exception getting content provider"); + } + + // registerInstance called at proxy, but not yet at original? + if ( m_xProvider.is() && m_bRegister ) + { + Reference< XParameterizedContentProvider > + xParamProvider( m_xProvider, UNO_QUERY ); + if ( xParamProvider.is() ) + { + try + { + m_xTargetProvider + = xParamProvider->registerInstance( m_aTemplate, + m_aArguments, + m_bReplace ); + } + catch ( IllegalIdentifierException const & ) + { + OSL_FAIL( "UcbContentProviderProxy::getContentProvider - " + "Caught IllegalIdentifierException!" ); + } + + OSL_ENSURE( m_xTargetProvider.is(), + "UcbContentProviderProxy::getContentProvider - " + "No provider!" ); + } + } + if ( !m_xTargetProvider.is() ) + m_xTargetProvider = m_xProvider; + } + + OSL_ENSURE( m_xProvider.is(), + OStringBuffer("UcbContentProviderProxy::getContentProvider - No provider for '" + + OUStringToOString(m_aService, osl_getThreadTextEncoding()) + + ".").getStr() ); + return m_xTargetProvider; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/provprox.hxx b/ucb/source/core/provprox.hxx new file mode 100644 index 000000000..1654fa342 --- /dev/null +++ b/ucb/source/core/provprox.hxx @@ -0,0 +1,126 @@ +/* -*- 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 <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/ucb/XContentProviderFactory.hpp> +#include <com/sun/star/ucb/XContentProvider.hpp> +#include <com/sun/star/ucb/XParameterizedContentProvider.hpp> +#include <com/sun/star/ucb/XContentProviderSupplier.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/compbase.hxx> +#include <cppuhelper/weak.hxx> + + + +using UcbContentProviderProxyFactory_Base = comphelper::WeakComponentImplHelper < + css::lang::XServiceInfo, + css::ucb::XContentProviderFactory >; +class UcbContentProviderProxyFactory : public UcbContentProviderProxyFactory_Base +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + +public: + explicit UcbContentProviderProxyFactory( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~UcbContentProviderProxyFactory() override; + + // 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; + + // XContentProviderFactory + virtual css::uno::Reference< css::ucb::XContentProvider > SAL_CALL + createContentProvider( const OUString& Service ) override; +}; + + + + +class UcbContentProviderProxy : + public cppu::OWeakObject, + public css::lang::XTypeProvider, + public css::lang::XServiceInfo, + public css::ucb::XContentProviderSupplier, + public css::ucb::XContentProvider, + public css::ucb::XParameterizedContentProvider +{ + std::mutex m_aMutex; + OUString m_aService; + OUString m_aTemplate; + OUString m_aArguments; + bool m_bReplace; + bool m_bRegister; + + css::uno::Reference< css::uno::XComponentContext > + m_xContext; + css::uno::Reference< css::ucb::XContentProvider > + m_xProvider; + css::uno::Reference< css::ucb::XContentProvider > + m_xTargetProvider; + +public: + UcbContentProviderProxy( + const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const OUString& Service ); + virtual ~UcbContentProviderProxy() 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< sal_Int8 > SAL_CALL getImplementationId() override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + + // 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; + + // XContentProviderSupplier + virtual css::uno::Reference< + css::ucb::XContentProvider > SAL_CALL + getContentProvider() override; + + // XContentProvider + virtual css::uno::Reference< + css::ucb::XContent > SAL_CALL + queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override; + virtual sal_Int32 SAL_CALL + compareContentIds( const css::uno::Reference< css::ucb::XContentIdentifier >& Id1, + const css::uno::Reference< css::ucb::XContentIdentifier >& Id2 ) override; + + // XParameterizedContentProvider + virtual css::uno::Reference< css::ucb::XContentProvider > SAL_CALL + registerInstance( const OUString& Template, + const OUString& Arguments, + sal_Bool ReplaceExisting ) override; + virtual css::uno::Reference< css::ucb::XContentProvider > SAL_CALL + deregisterInstance( const OUString& Template, + const OUString& Arguments ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucb.cxx b/ucb/source/core/ucb.cxx new file mode 100644 index 000000000..4e41e8cff --- /dev/null +++ b/ucb/source/core/ucb.cxx @@ -0,0 +1,887 @@ +/* -*- 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 + ************************************************************************** + + *************************************************************************/ + +#include <sal/config.h> + +#include <string_view> + +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/DuplicateProviderException.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XContentProviderSupplier.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <tools/diagnose_ex.h> +#include "identify.hxx" +#include "ucbcmds.hxx" + +#include "ucb.hxx" + +using namespace comphelper; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace ucb_impl; +using namespace com::sun::star; +using namespace ucbhelper; + +namespace { + +bool fillPlaceholders(OUString const & rInput, + uno::Sequence< uno::Any > const & rReplacements, + OUString * pOutput) +{ + sal_Unicode const * p = rInput.getStr(); + sal_Unicode const * pEnd = p + rInput.getLength(); + sal_Unicode const * pCopy = p; + OUStringBuffer aBuffer; + while (p != pEnd) + switch (*p++) + { + case '&': + if (pEnd - p >= 4 + && p[0] == 'a' && p[1] == 'm' && p[2] == 'p' + && p[3] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('&'); + p += 4; + pCopy = p; + } + else if (pEnd - p >= 3 + && p[0] == 'l' && p[1] == 't' && p[2] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('<'); + p += 3; + pCopy = p; + } + else if (pEnd - p >= 3 + && p[0] == 'g' && p[1] == 't' && p[2] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('>'); + p += 3; + pCopy = p; + } + break; + + case '<': + sal_Unicode const * q = p; + while (q != pEnd && *q != '>') + ++q; + if (q == pEnd) + break; + OUString aKey(p, q - p); + OUString aValue; + bool bFound = false; + for (sal_Int32 i = 2; i + 1 < rReplacements.getLength(); + i += 2) + { + OUString aReplaceKey; + if ((rReplacements[i] >>= aReplaceKey) + && aReplaceKey == aKey + && (rReplacements[i + 1] >>= aValue)) + { + bFound = true; + break; + } + } + if (!bFound) + return false; + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append(aValue); + p = q + 1; + pCopy = p; + break; + } + aBuffer.append(pCopy, pEnd - pCopy); + *pOutput = aBuffer.makeStringAndClear(); + return true; +} + +void makeAndAppendXMLName( + OUStringBuffer & rBuffer, const OUString & rIn ) +{ + sal_Int32 nCount = rIn.getLength(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const sal_Unicode c = rIn[ n ]; + switch ( c ) + { + case '&': + rBuffer.append( "&" ); + break; + + case '"': + rBuffer.append( """ ); + break; + + case '\'': + rBuffer.append( "'" ); + break; + + case '<': + rBuffer.append( "<" ); + break; + + case '>': + rBuffer.append( ">" ); + break; + + default: + rBuffer.append( c ); + break; + } + } +} + +bool createContentProviderData( + std::u16string_view rProvider, + const uno::Reference< container::XHierarchicalNameAccess >& rxHierNameAccess, + ContentProviderData & rInfo) +{ + // Obtain service name. + + OUString aValue; + try + { + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/ServiceName" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + } + catch (const container::NoSuchElementException&) + { + return false; + } + + rInfo.ServiceName = aValue; + + // Obtain URL Template. + + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/URLTemplate" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + + rInfo.URLTemplate = aValue; + + // Obtain Arguments. + + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/Arguments" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + + rInfo.Arguments = aValue; + return true; +} + +} + + +// UniversalContentBroker Implementation. + + +UniversalContentBroker::UniversalContentBroker( + const Reference< css::uno::XComponentContext >& xContext ) +: m_xContext( xContext ), + m_nCommandId( 0 ) +{ + OSL_ENSURE( m_xContext.is(), + "UniversalContentBroker ctor: No service manager" ); +} + + +// virtual +UniversalContentBroker::~UniversalContentBroker() +{ +} + + +// XComponent methods. + + +// virtual +void SAL_CALL UniversalContentBroker::dispose() +{ + if ( m_pDisposeEventListeners && m_pDisposeEventListeners->getLength() ) + { + EventObject aEvt; + aEvt.Source = static_cast< XComponent* >(this); + m_pDisposeEventListeners->disposeAndClear( aEvt ); + } + + if ( m_xNotifier.is() ) + m_xNotifier->removeChangesListener( this ); +} + + +// virtual +void SAL_CALL UniversalContentBroker::addEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( !m_pDisposeEventListeners ) + m_pDisposeEventListeners.reset( new OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutex ) ); + + m_pDisposeEventListeners->addInterface( Listener ); +} + + +// virtual +void SAL_CALL UniversalContentBroker::removeEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( m_pDisposeEventListeners ) + m_pDisposeEventListeners->removeInterface( Listener ); + + // Note: Don't want to delete empty container here -> performance. +} + + +// XServiceInfo methods. + +OUString SAL_CALL UniversalContentBroker::getImplementationName() +{ + return "com.sun.star.comp.ucb.UniversalContentBroker"; +} +sal_Bool SAL_CALL UniversalContentBroker::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} +css::uno::Sequence< OUString > SAL_CALL UniversalContentBroker::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.UniversalContentBroker" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UniversalContentBroker_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UniversalContentBroker(context)); +} + + +// XInitialization methods. + + +// virtual +void SAL_CALL UniversalContentBroker::initialize( const css::uno::Sequence< Any >& aArguments ) +{ + { + osl::MutexGuard aGuard(m_aMutex); + if (m_aArguments.hasElements()) + { + if (aArguments.hasElements() + && !(m_aArguments.getLength() == 2 + && aArguments.getLength() == 2 + && m_aArguments[0] == aArguments[0] + && m_aArguments[1] == aArguments[1])) + { + throw IllegalArgumentException( + "UCB reinitialized with different arguments", + static_cast< cppu::OWeakObject * >(this), 0); + } + return; + } + if (!aArguments.hasElements()) + { + m_aArguments = { Any(OUString("Local")), Any(OUString("Office")) }; + } + else + { + m_aArguments = aArguments; + } + } + configureUcb(); +} + + +// XContentProviderManager methods. + + +// virtual +Reference< XContentProvider > SAL_CALL +UniversalContentBroker::registerContentProvider( + const Reference< XContentProvider >& Provider, + const OUString& Scheme, + sal_Bool ReplaceExisting ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ProviderMap_Impl::iterator aIt; + try + { + aIt = m_aProviders.find(Scheme); + } + catch (const IllegalArgumentException&) + { + return nullptr; //@@@ + } + + Reference< XContentProvider > xPrevious; + if (aIt == m_aProviders.end()) + { + ProviderList_Impl aList; + aList.push_front( ProviderListEntry_Impl(Provider) ); + try + { + m_aProviders.add(Scheme, aList); + } + catch (const IllegalArgumentException&) + { + return nullptr; //@@@ + } + } + else + { + if (!ReplaceExisting) + throw DuplicateProviderException(); + + ProviderList_Impl & rList = aIt->getValue(); + xPrevious = rList.front().getProvider(); + rList.push_front( ProviderListEntry_Impl(Provider) ); + } + + return xPrevious; +} + + +// virtual +void SAL_CALL UniversalContentBroker::deregisterContentProvider( + const Reference< XContentProvider >& Provider, + const OUString& Scheme ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ProviderMap_Impl::iterator aMapIt; + try + { + aMapIt = m_aProviders.find(Scheme); + } + catch (const IllegalArgumentException&) + { + return; //@@@ + } + + if (aMapIt != m_aProviders.end()) + { + ProviderList_Impl & rList = aMapIt->getValue(); + + auto aListIt = std::find_if(rList.begin(), rList.end(), + [&Provider](const ProviderListEntry_Impl& rEntry) { return rEntry.getProvider() == Provider; }); + if (aListIt != rList.end()) + rList.erase(aListIt); + + if (rList.empty()) + m_aProviders.erase(aMapIt); + } +} + + +// virtual +css::uno::Sequence< ContentProviderInfo > SAL_CALL + UniversalContentBroker::queryContentProviders() +{ + // Return a list with information about active(!) content providers. + + osl::MutexGuard aGuard(m_aMutex); + + css::uno::Sequence< ContentProviderInfo > aSeq( m_aProviders.size() ); + ContentProviderInfo* pInfo = aSeq.getArray(); + + ProviderMap_Impl::const_iterator end = m_aProviders.end(); + for (ProviderMap_Impl::const_iterator it(m_aProviders.begin()); it != end; + ++it) + { + // Note: Active provider is always the first list element. + pInfo->ContentProvider = it->getValue().front().getProvider(); + pInfo->Scheme = it->getRegexp(); + ++pInfo; + } + + return aSeq; +} + + +// virtual +Reference< XContentProvider > SAL_CALL + UniversalContentBroker::queryContentProvider( const OUString& + Identifier ) +{ + return queryContentProvider( Identifier, false ); +} + + +// XContentProvider methods. + + +// virtual +Reference< XContent > SAL_CALL UniversalContentBroker::queryContent( + const Reference< XContentIdentifier >& Identifier ) +{ + + // Let the content provider for the scheme given with the content + // identifier create the XContent instance. + + + if ( !Identifier.is() ) + return Reference< XContent >(); + + Reference< XContentProvider > xProv = + queryContentProvider( Identifier->getContentIdentifier(), true ); + if ( xProv.is() ) + return xProv->queryContent( Identifier ); + + return Reference< XContent >(); +} + + +// virtual +sal_Int32 SAL_CALL UniversalContentBroker::compareContentIds( + const Reference< XContentIdentifier >& Id1, + const Reference< XContentIdentifier >& Id2 ) +{ + OUString aURI1( Id1->getContentIdentifier() ); + OUString aURI2( Id2->getContentIdentifier() ); + + Reference< XContentProvider > xProv1 + = queryContentProvider( aURI1, true ); + Reference< XContentProvider > xProv2 + = queryContentProvider( aURI2, true ); + + // When both identifiers belong to the same provider, let that provider + // compare them; otherwise, simply compare the URI strings (which must + // be different): + if ( xProv1.is() && ( xProv1 == xProv2 ) ) + return xProv1->compareContentIds( Id1, Id2 ); + else + return aURI1.compareTo( aURI2 ); +} + + +// XContentIdentifierFactory methods. + + +// virtual +Reference< XContentIdentifier > SAL_CALL + UniversalContentBroker::createContentIdentifier( + const OUString& ContentId ) +{ + + // Let the content provider for the scheme given with content + // identifier create the XContentIdentifier instance, if he supports + // the XContentIdentifierFactory interface. Otherwise create standard + // implementation object for XContentIdentifier. + + + Reference< XContentIdentifier > xIdentifier; + + Reference< XContentProvider > xProv + = queryContentProvider( ContentId, true ); + if ( xProv.is() ) + { + Reference< XContentIdentifierFactory > xFac( xProv, UNO_QUERY ); + if ( xFac.is() ) + xIdentifier = xFac->createContentIdentifier( ContentId ); + } + + if ( !xIdentifier.is() ) + xIdentifier = new ContentIdentifier( ContentId ); + + return xIdentifier; +} + + +// XCommandProcessor methods. + + +// virtual +sal_Int32 SAL_CALL UniversalContentBroker::createCommandIdentifier() +{ + osl::MutexGuard aGuard( m_aMutex ); + + // Just increase counter on every call to generate an identifier. + return ++m_nCommandId; +} + + +// virtual +Any SAL_CALL UniversalContentBroker::execute( + const Command& aCommand, + sal_Int32, + const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + + + // Note: Don't forget to adapt ucb_commands::CommandProcessorInfo + // ctor in ucbcmds.cxx when adding new commands! + + + if ( ( aCommand.Handle == GETCOMMANDINFO_HANDLE ) || aCommand.Name == GETCOMMANDINFO_NAME ) + { + + // getCommandInfo + + + aRet <<= getCommandInfo(); + } + else if ( ( aCommand.Handle == GLOBALTRANSFER_HANDLE ) || aCommand.Name == GLOBALTRANSFER_NAME ) + { + + // globalTransfer + + + GlobalTransferCommandArgument2 aTransferArg; + if ( !( aCommand.Argument >>= aTransferArg ) ) + { + GlobalTransferCommandArgument aArg; + if ( !( aCommand.Argument >>= aArg ) ) + { + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + "Wrong argument type!", + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + // Copy infos into the new structure + aTransferArg.Operation = aArg.Operation; + aTransferArg.SourceURL = aArg.SourceURL; + aTransferArg.TargetURL = aArg.TargetURL; + aTransferArg.NewTitle = aArg.NewTitle; + aTransferArg.NameClash = aArg.NameClash; + } + + globalTransfer( aTransferArg, Environment ); + } + else if ( ( aCommand.Handle == CHECKIN_HANDLE ) || aCommand.Name == CHECKIN_NAME ) + { + ucb::CheckinArgument aCheckinArg; + if ( !( aCommand.Argument >>= aCheckinArg ) ) + { + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + "Wrong argument type!", + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + aRet = checkIn( aCheckinArg, Environment ); + } + else + { + + // Unknown command + + + ucbhelper::cancelCommandExecution( + Any( UnsupportedCommandException( + OUString(), + static_cast< cppu::OWeakObject * >( this ) ) ), + Environment ); + // Unreachable + } + + return aRet; +} + + +// XCommandProcessor2 methods. + + +// virtual +void SAL_CALL UniversalContentBroker::releaseCommandIdentifier(sal_Int32 /*aCommandId*/) +{ + // @@@ Not implemented ( yet). +} + + +// virtual +void SAL_CALL UniversalContentBroker::abort( sal_Int32 ) +{ + // @@@ Not implemented ( yet). +} + + +// XChangesListener methods + + +// virtual +void SAL_CALL UniversalContentBroker::changesOccurred( const util::ChangesEvent& Event ) +{ + if ( !Event.Changes.hasElements() ) + return; + + uno::Reference< container::XHierarchicalNameAccess > xHierNameAccess; + Event.Base >>= xHierNameAccess; + + OSL_ASSERT( xHierNameAccess.is() ); + + ContentProviderDataList aData; + for ( const util::ElementChange& rElem : Event.Changes ) + { + OUString aKey; + rElem.Accessor >>= aKey; + + ContentProviderData aInfo; + + // Removal of UCPs from the configuration leads to changesOccurred + // notifications, too, but it is hard to tell for a given + // ElementChange whether it is an addition or a removal, so as a + // heuristic consider as removals those that cause a + // NoSuchElementException in createContentProviderData. + + // For now, removal of UCPs from the configuration is simply ignored + // (and not reflected in the UCB's data structures): + if (createContentProviderData(aKey, xHierNameAccess, aInfo)) + { + aData.push_back(aInfo); + } + } + + prepareAndRegister(aData); +} + + +// XEventListener methods + + +// virtual +void SAL_CALL UniversalContentBroker::disposing(const lang::EventObject&) +{ + if ( m_xNotifier.is() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( m_xNotifier.is() ) + m_xNotifier.clear(); + } +} + + +// Non-interface methods + + +Reference< XContentProvider > UniversalContentBroker::queryContentProvider( + const OUString& Identifier, + bool bResolved ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + ProviderList_Impl const * pList = m_aProviders.map( Identifier ); + return pList ? bResolved ? pList->front().getResolvedProvider() + : pList->front().getProvider() + : Reference< XContentProvider >(); +} + +void UniversalContentBroker::configureUcb() +{ + OUString aKey1; + OUString aKey2; + if (m_aArguments.getLength() < 2 + || !(m_aArguments[0] >>= aKey1) || !(m_aArguments[1] >>= aKey2)) + { + OSL_FAIL("UniversalContentBroker::configureUcb(): Bad arguments"); + return; + } + + ContentProviderDataList aData; + if (!getContentProviderData(aKey1, aKey2, aData)) + { + SAL_WARN( "ucb", "No configuration"); + return; + } + + prepareAndRegister(aData); +} + +void UniversalContentBroker::prepareAndRegister( + const ContentProviderDataList& rData) +{ + for (const auto& rContentProviderData : rData) + { + OUString aProviderArguments; + if (fillPlaceholders(rContentProviderData.Arguments, + m_aArguments, + &aProviderArguments)) + { + registerAtUcb(this, + m_xContext, + rContentProviderData.ServiceName, + aProviderArguments, + rContentProviderData.URLTemplate); + + } + else + OSL_FAIL("UniversalContentBroker::prepareAndRegister(): Bad argument placeholders"); + } +} + + +bool UniversalContentBroker::getContentProviderData( + const OUString & rKey1, + const OUString & rKey2, + ContentProviderDataList & rListToFill ) +{ + if ( !m_xContext.is() || rKey1.isEmpty() || rKey2.isEmpty() ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - Invalid argument!" ); + return false; + } + + try + { + uno::Reference< lang::XMultiServiceFactory > xConfigProv = + configuration::theDefaultProvider::get( m_xContext ); + + OUStringBuffer aFullPath(128); + aFullPath.append( + "/org.openoffice.ucb.Configuration/ContentProviders" + "/['" ); + makeAndAppendXMLName( aFullPath, rKey1 ); + aFullPath.append( "']/SecondaryKeys/['" ); + makeAndAppendXMLName( aFullPath, rKey2 ); + aFullPath.append( "']/ProviderData" ); + + uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(aFullPath.makeStringAndClear())} + })); + + uno::Reference< uno::XInterface > xInterface( + xConfigProv->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments ) ); + + if ( !m_xNotifier.is() ) + { + m_xNotifier.set( xInterface, uno::UNO_QUERY_THROW ); + + m_xNotifier->addChangesListener( this ); + } + + uno::Reference< container::XNameAccess > xNameAccess( + xInterface, uno::UNO_QUERY_THROW ); + + const uno::Sequence< OUString > aElems = xNameAccess->getElementNames(); + + if ( aElems.hasElements() ) + { + uno::Reference< container::XHierarchicalNameAccess > + xHierNameAccess( xInterface, uno::UNO_QUERY_THROW ); + + // Iterate over children. + for ( const auto& rElem : aElems ) + { + + try + { + + ContentProviderData aInfo; + + OUStringBuffer aElemBuffer; + aElemBuffer.append( "['" ); + makeAndAppendXMLName( aElemBuffer, rElem ); + aElemBuffer.append( "']" ); + + OSL_VERIFY( + createContentProviderData( + aElemBuffer.makeStringAndClear(), xHierNameAccess, + aInfo)); + + rListToFill.push_back( aInfo ); + } + catch (const container::NoSuchElementException&) + { + // getByHierarchicalName + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "caught NoSuchElementException!" ); + } + } + } + } + catch (const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION( "ucb", "" ); + return false; + } + catch (const uno::Exception&) + { + // createInstance, createInstanceWithArguments + + TOOLS_WARN_EXCEPTION( "ucb", "" ); + return false; + } + + return true; +} + + +// ProviderListEntry_Impl implementation. + + +Reference< XContentProvider > const & ProviderListEntry_Impl::resolveProvider() const +{ + if ( !m_xResolvedProvider.is() ) + { + Reference< XContentProviderSupplier > xSupplier( + m_xProvider, UNO_QUERY ); + if ( xSupplier.is() ) + m_xResolvedProvider = xSupplier->getContentProvider(); + + if ( !m_xResolvedProvider.is() ) + m_xResolvedProvider = m_xProvider; + } + + return m_xResolvedProvider; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucb.hxx b/ucb/source/core/ucb.hxx new file mode 100644 index 000000000..49c6e0b37 --- /dev/null +++ b/ucb/source/core/ucb.hxx @@ -0,0 +1,158 @@ +/* -*- 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 <com/sun/star/ucb/CheckinArgument.hpp> +#include <com/sun/star/ucb/XUniversalContentBroker.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/util/XChangesListener.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> + +#include <comphelper/interfacecontainer3.hxx> +#include <cppuhelper/implbase.hxx> +#include <osl/mutex.hxx> +#include "providermap.hxx" +#include <ucbhelper/registerucb.hxx> + +#include <memory> + + +namespace com::sun::star::ucb { + class XCommandInfo; + struct GlobalTransferCommandArgument2; +} + +class UniversalContentBroker : + public cppu::WeakImplHelper< + css::ucb::XUniversalContentBroker, + css::lang::XServiceInfo, + css::lang::XInitialization, + css::util::XChangesListener> +{ +public: + explicit UniversalContentBroker( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~UniversalContentBroker() override; + + // 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; + + // XComponent + virtual void SAL_CALL + dispose() override; + virtual void SAL_CALL + addEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) override; + virtual void SAL_CALL + removeEventListener( const css::uno::Reference< + css::lang::XEventListener >& Listener ) override; + + // XInitialization + virtual void SAL_CALL + initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XContentProviderManager + virtual css::uno::Reference< css::ucb::XContentProvider > SAL_CALL + registerContentProvider( const css::uno::Reference< css::ucb::XContentProvider >& Provider, + const OUString& Scheme, + sal_Bool ReplaceExisting ) override; + virtual void SAL_CALL + deregisterContentProvider( const css::uno::Reference< css::ucb::XContentProvider >& Provider, + const OUString& Scheme ) override; + virtual css::uno::Sequence< css::ucb::ContentProviderInfo > SAL_CALL + queryContentProviders() override; + virtual css::uno::Reference< css::ucb::XContentProvider > SAL_CALL + queryContentProvider( const OUString& Identifier ) override; + + // XContentProvider + virtual css::uno::Reference< css::ucb::XContent > SAL_CALL + queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override; + virtual sal_Int32 SAL_CALL + compareContentIds( const css::uno::Reference< css::ucb::XContentIdentifier >& Id1, + const css::uno::Reference< css::ucb::XContentIdentifier >& Id2 ) override; + + // XContentIdentifierFactory + virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL + createContentIdentifier( const OUString& ContentId ) override; + + // XCommandProcessor + virtual sal_Int32 SAL_CALL + createCommandIdentifier() override; + virtual css::uno::Any SAL_CALL + execute( const css::ucb::Command& aCommand, + sal_Int32 CommandId, + const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override; + virtual void SAL_CALL + abort( sal_Int32 CommandId ) override; + + // XCommandProcessor2 + virtual void SAL_CALL releaseCommandIdentifier(sal_Int32 aCommandId) override; + + // XChangesListener + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + + // XEventListener ( base of XChangesLisetenr ) + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + +private: + css::uno::Reference< css::ucb::XContentProvider > + queryContentProvider( const OUString& Identifier, + bool bResolved ); + + static css::uno::Reference< css::ucb::XCommandInfo > + getCommandInfo(); + + /// @throws css::uno::Exception + void + globalTransfer( + const css::ucb::GlobalTransferCommandArgument2 & rArg, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ); + + /// @throws css::uno::Exception + css::uno::Any checkIn( const css::ucb::CheckinArgument& rArg, + const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv ); + + + /// @throws css::uno::RuntimeException + void configureUcb(); + + bool getContentProviderData( + const OUString & rKey1, + const OUString & rKey2, + ucbhelper::ContentProviderDataList & rListToFill); + + void prepareAndRegister( const ucbhelper::ContentProviderDataList& rData); + + css::uno::Reference< + css::uno::XComponentContext > m_xContext; + + css::uno::Reference< + css::util::XChangesNotifier > m_xNotifier; + + css::uno::Sequence< css::uno::Any > m_aArguments; + ProviderMap_Impl m_aProviders; + osl::Mutex m_aMutex; + std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pDisposeEventListeners; + sal_Int32 m_nCommandId; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucb1.component b/ucb/source/core/ucb1.component new file mode 100644 index 000000000..952d37406 --- /dev/null +++ b/ucb/source/core/ucb1.component @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.ucb.CommandEnvironment" + constructor="ucb_UcbCommandEnvironment_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.CommandEnvironment"/> + </implementation> + <implementation name="com.sun.star.comp.ucb.UcbContentProviderProxyFactory" + constructor="ucb_UcbContentProviderProxyFactory_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.ContentProviderProxyFactory"/> + </implementation> + <implementation name="com.sun.star.comp.ucb.UcbPropertiesManager" + constructor="ucb_UcbPropertiesManager_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.PropertiesManager"/> + </implementation> + <implementation name="com.sun.star.comp.ucb.UcbStore" + constructor="ucb_UcbStore_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.Store"/> + </implementation> + <implementation name="com.sun.star.comp.ucb.UniversalContentBroker" + constructor="ucb_UniversalContentBroker_get_implementation" single-instance="true"> + <service name="com.sun.star.ucb.UniversalContentBroker"/> + </implementation> + <implementation name="com.sun.star.comp.ucb.SimpleFileAccess" + constructor="ucb_OFileAccess_get_implementation"> + <service name="com.sun.star.ucb.SimpleFileAccess"/> + </implementation> +</component> diff --git a/ucb/source/core/ucbcmds.cxx b/ucb/source/core/ucbcmds.cxx new file mode 100644 index 000000000..f0e45b7cb --- /dev/null +++ b/ucb/source/core/ucbcmds.cxx @@ -0,0 +1,1923 @@ +/* -*- 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 <osl/diagnose.h> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/Pipe.hpp> +#include <com/sun/star/io/XActiveDataSink.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/CommandEnvironment.hpp> +#include <com/sun/star/ucb/CommandFailedException.hpp> +#include <com/sun/star/ucb/ContentInfoAttribute.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp> +#include <com/sun/star/ucb/IllegalIdentifierException.hpp> +#include <com/sun/star/ucb/InsertCommandArgument2.hpp> +#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp> +#include <com/sun/star/ucb/NameClash.hpp> +#include <com/sun/star/ucb/NameClashException.hpp> +#include <com/sun/star/ucb/OpenCommandArgument2.hpp> +#include <com/sun/star/ucb/OpenMode.hpp> +#include <com/sun/star/ucb/TransferInfo2.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/UnsupportedNameClashException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XContentCreator.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/ucb/XInteractionSupplyName.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <ucbhelper/simplenameclashresolverequest.hxx> +#include "ucbcmds.hxx" +#include "ucb.hxx" + +using namespace com::sun::star; + +namespace +{ +// Helper to provide defaults for type and attributes (save some typing) +beans::Property makeProperty(const OUString& n, sal_Int32 h, uno::Type t = {}, sal_Int16 a = {}) +{ + return { n, h, t, a }; +} + +// struct TransferCommandContext. + + +struct TransferCommandContext +{ + uno::Reference< uno::XComponentContext > m_xContext; + uno::Reference< ucb::XCommandProcessor > xProcessor; + uno::Reference< ucb::XCommandEnvironment > xEnv; + uno::Reference< ucb::XCommandEnvironment > xOrigEnv; + ucb::GlobalTransferCommandArgument2 aArg; + + TransferCommandContext( + const uno::Reference< uno::XComponentContext > & xContext, + const uno::Reference< ucb::XCommandProcessor > & rxProcessor, + const uno::Reference< ucb::XCommandEnvironment > & rxEnv, + const uno::Reference< ucb::XCommandEnvironment > & rxOrigEnv, + const ucb::GlobalTransferCommandArgument2 & rArg ) + : m_xContext( xContext ), xProcessor( rxProcessor ), xEnv( rxEnv ), + xOrigEnv( rxOrigEnv ), aArg( rArg ) {} +}; + + + + +class InteractionHandlerProxy : + public cppu::WeakImplHelper< task::XInteractionHandler > +{ + uno::Reference< task::XInteractionHandler > m_xOrig; + +public: + explicit InteractionHandlerProxy( + const uno::Reference< task::XInteractionHandler > & xOrig ) + : m_xOrig( xOrig ) {} + + // XInteractionHandler methods. + virtual void SAL_CALL handle( + const uno::Reference< task::XInteractionRequest >& Request ) override; +}; + + +// virtual +void SAL_CALL InteractionHandlerProxy::handle( + const uno::Reference< task::XInteractionRequest >& Request ) +{ + if ( !m_xOrig.is() ) + return; + + // Filter unwanted requests by just not handling them. + uno::Any aRequest = Request->getRequest(); + + // "transfer" + ucb::InteractiveBadTransferURLException aBadTransferURLEx; + if ( aRequest >>= aBadTransferURLEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedNameClashException aUnsupportedNameClashEx; + if ( aRequest >>= aUnsupportedNameClashEx ) + { + if ( aUnsupportedNameClashEx.NameClash + != ucb::NameClash::ERROR ) + return; + } + else + { + // "insert" + ucb::NameClashException aNameClashEx; + if ( aRequest >>= aNameClashEx ) + { + return; + } + else + { + // "transfer" + ucb::UnsupportedCommandException aUnsupportedCommandEx; + if ( aRequest >>= aUnsupportedCommandEx ) + { + return; + } + } + } + } + + // not filtered; let the original handler do the work. + m_xOrig->handle( Request ); +} + + + + +class ActiveDataSink : public cppu::WeakImplHelper< io::XActiveDataSink > +{ + uno::Reference< io::XInputStream > m_xStream; + +public: + // XActiveDataSink methods. + virtual void SAL_CALL setInputStream( + const uno::Reference< io::XInputStream >& aStream ) override; + virtual uno::Reference< io::XInputStream > SAL_CALL getInputStream() override; +}; + + +// virtual +void SAL_CALL ActiveDataSink::setInputStream( + const uno::Reference< io::XInputStream >& aStream ) +{ + m_xStream = aStream; +} + + +// virtual +uno::Reference< io::XInputStream > SAL_CALL ActiveDataSink::getInputStream() +{ + return m_xStream; +} + + + + +class CommandProcessorInfo : + public cppu::WeakImplHelper< ucb::XCommandInfo > +{ + uno::Sequence< ucb::CommandInfo > m_xInfo; + +public: + CommandProcessorInfo(); + + // XCommandInfo methods + virtual uno::Sequence< ucb::CommandInfo > SAL_CALL getCommands() override; + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByName( const OUString& Name ) override; + virtual ucb::CommandInfo SAL_CALL + getCommandInfoByHandle( sal_Int32 Handle ) override; + virtual sal_Bool SAL_CALL hasCommandByName( const OUString& Name ) override; + virtual sal_Bool SAL_CALL hasCommandByHandle( sal_Int32 Handle ) override; +}; + + +CommandProcessorInfo::CommandProcessorInfo() + : m_xInfo{ + ucb::CommandInfo( + GETCOMMANDINFO_NAME, // Name + GETCOMMANDINFO_HANDLE, // Handle + cppu::UnoType<void>::get() ), // ArgType + ucb::CommandInfo( + GLOBALTRANSFER_NAME, // Name + GLOBALTRANSFER_HANDLE, // Handle + cppu::UnoType<ucb::GlobalTransferCommandArgument>::get() ), // ArgType + ucb::CommandInfo( + CHECKIN_NAME, // Name + CHECKIN_HANDLE, // Handle + cppu::UnoType<ucb::CheckinArgument>::get() ) } // ArgType +{ +} + + +// virtual +uno::Sequence< ucb::CommandInfo > SAL_CALL +CommandProcessorInfo::getCommands() +{ + return m_xInfo; +} + + +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByName( const OUString& Name ) +{ + auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; }); + if (pInfo != std::cend(m_xInfo)) + return *pInfo; + + throw ucb::UnsupportedCommandException(); +} + + +// virtual +ucb::CommandInfo SAL_CALL +CommandProcessorInfo::getCommandInfoByHandle( sal_Int32 Handle ) +{ + auto pInfo = std::find_if(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; }); + if (pInfo != std::cend(m_xInfo)) + return *pInfo; + + throw ucb::UnsupportedCommandException(); +} + + +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByName( + const OUString& Name ) +{ + return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Name](const ucb::CommandInfo& rInfo) { return rInfo.Name == Name; }); +} + + +// virtual +sal_Bool SAL_CALL CommandProcessorInfo::hasCommandByHandle( sal_Int32 Handle ) +{ + return std::any_of(std::cbegin(m_xInfo), std::cend(m_xInfo), + [&Handle](const ucb::CommandInfo& rInfo) { return rInfo.Handle == Handle; }); +} + + +OUString createDesiredName( + const OUString & rSourceURL, const OUString & rNewTitle ) +{ + OUString aName( rNewTitle ); + if ( aName.isEmpty() ) + { + // calculate name using source URL + + // @@@ It's not guaranteed that slashes contained in the URL are + // actually path separators. This depends on the fact whether the + // URL is hierarchical. Only then the slashes are path separators. + // Therefore this algorithm is not guaranteed to work! But, ATM + // I don't know a better solution. It would have been better to + // have a member for the clashing name in + // UnsupportedNameClashException... + + sal_Int32 nLastSlash = rSourceURL.lastIndexOf( '/' ); + bool bTrailingSlash = false; + if ( nLastSlash == rSourceURL.getLength() - 1 ) + { + nLastSlash = rSourceURL.lastIndexOf( '/', nLastSlash ); + bTrailingSlash = true; + } + + if ( nLastSlash != -1 ) + { + if ( bTrailingSlash ) + aName = rSourceURL.copy( + nLastSlash + 1, + rSourceURL.getLength() - nLastSlash - 2 ); + else + aName = rSourceURL.copy( nLastSlash + 1 ); + } + else + { + aName = rSourceURL; + } + + // query, fragment present? + sal_Int32 nPos = aName.indexOf( '?' ); + if ( nPos == -1 ) + nPos = aName.indexOf( '#' ); + + if ( nPos != -1 ) + aName = aName.copy( 0, nPos ); + } + return aName; +} + +OUString createDesiredName( + const ucb::GlobalTransferCommandArgument & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + +OUString createDesiredName( + const ucb::TransferInfo & rArg ) +{ + return createDesiredName( rArg.SourceURL, rArg.NewTitle ); +} + + +enum NameClashContinuation { NOT_HANDLED, ABORT, OVERWRITE, NEW_NAME, UNKNOWN }; + +NameClashContinuation interactiveNameClashResolve( + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const OUString & rTargetURL, + const OUString & rClashingName, + /* [out] */ uno::Any & rException, + /* [out] */ OUString & rNewName ) +{ + rtl::Reference< ucbhelper::SimpleNameClashResolveRequest > xRequest( + new ucbhelper::SimpleNameClashResolveRequest( + rTargetURL, // target folder URL + rClashingName + ) ); + + rException = xRequest->getRequest(); + if ( xEnv.is() ) + { + uno::Reference< task::XInteractionHandler > xIH + = xEnv->getInteractionHandler(); + if ( xIH.is() ) + { + + xIH->handle( xRequest ); + + rtl::Reference< ucbhelper::InteractionContinuation > + xSelection( xRequest->getSelection() ); + + if ( xSelection.is() ) + { + // Handler handled the request. + uno::Reference< task::XInteractionAbort > xAbort( + xSelection.get(), uno::UNO_QUERY ); + if ( xAbort.is() ) + { + // Abort. + return ABORT; + } + else + { + uno::Reference< + ucb::XInteractionReplaceExistingData > + xReplace( + xSelection.get(), uno::UNO_QUERY ); + if ( xReplace.is() ) + { + // Try again: Replace existing data. + return OVERWRITE; + } + else + { + uno::Reference< + ucb::XInteractionSupplyName > + xSupplyName( + xSelection.get(), uno::UNO_QUERY ); + if ( xSupplyName.is() ) + { + // Try again: Use new name. + rNewName = xRequest->getNewName(); + return NEW_NAME; + } + else + { + OSL_FAIL( "Unknown interaction continuation!" ); + return UNKNOWN; + } + } + } + } + } + } + return NOT_HANDLED; +} + +/// @throws uno::RuntimeException +bool setTitle( + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessor, + const uno::Reference< ucb::XCommandEnvironment > & xEnv, + const OUString & rNewTitle ) +{ + try + { + uno::Sequence< beans::PropertyValue > aPropValues{ { /* Name */ "Title", + /* Handle */ -1, + /* Value */ uno::Any(rNewTitle), + /* State */ {} } }; + + ucb::Command aSetPropsCommand( + "setPropertyValues", + -1, + uno::Any( aPropValues ) ); + + uno::Any aResult + = xCommandProcessor->execute( aSetPropsCommand, 0, xEnv ); + + uno::Sequence< uno::Any > aErrors; + aResult >>= aErrors; + + OSL_ENSURE( aErrors.getLength() == 1, + "getPropertyValues return value invalid!" ); + + if ( aErrors[ 0 ].hasValue() ) + { + // error occurred. + OSL_FAIL( "error setting Title property!" ); + return false; + } + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + return false; + } + + return true; +} + +/// @throws uno::Exception +uno::Reference< ucb::XContent > createNew( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xTarget, + bool bSourceIsFolder, + bool bSourceIsDocument, + bool bSourceIsLink ) +{ + + + // (1) Obtain creatable types from target. + + + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorT( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessorT.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "Target is no XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aPropsToObtain{ makeProperty("CreatableContentsInfo", -1) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aPropsToObtain ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorT->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + uno::Sequence< ucb::ContentInfo > aTypesInfo; + bool bGotTypesInfo = false; + + if ( xRow.is() ) + { + uno::Any aValue = xRow->getObject( + 1, uno::Reference< container::XNameAccess >() ); + if ( aValue.hasValue() && ( aValue >>= aTypesInfo ) ) + { + bGotTypesInfo = true; + } + } + + uno::Reference< ucb::XContentCreator > xCreator; + + if ( !bGotTypesInfo ) + { + // Second, try it using XContentCreator interface -> the "old" way (not + // providing the chance to supply an XCommandEnvironment. + + xCreator.set( xTarget, uno::UNO_QUERY ); + + if ( !xCreator.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "Target is no XContentCreator!", + rContext.xProcessor ); + // Unreachable + } + + aTypesInfo = xCreator->queryCreatableContentsInfo(); + } + + if ( !aTypesInfo.hasElements() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "No types creatable!", + rContext.xProcessor ); + // Unreachable + } + + // (2) Try to find a matching target type for the source object. + + std::function<bool(const sal_Int32)> lCompare; + + if ( rContext.aArg.Operation == ucb::TransferCommandOperation_LINK ) + { + // Create link + lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); }; + } + else if ( ( rContext.aArg.Operation == ucb::TransferCommandOperation_COPY ) || + ( rContext.aArg.Operation == ucb::TransferCommandOperation_MOVE ) ) + { + // Copy / Move + // Is source a link? Create link in target folder then. + if ( bSourceIsLink ) + { + lCompare = [](const sal_Int32 nAttribs) { return !!( nAttribs & ucb::ContentInfoAttribute::KIND_LINK ); }; + } + else + { + // (not a and not b) or (a and b) + // not( a or b) or (a and b) + lCompare = [bSourceIsFolder, bSourceIsDocument](const sal_Int32 nAttribs) { + return ( bSourceIsFolder == !!( nAttribs & ucb::ContentInfoAttribute::KIND_FOLDER ) ) + && ( bSourceIsDocument == !!( nAttribs & ucb::ContentInfoAttribute::KIND_DOCUMENT ) ) ; + }; + } + } + else + { + ucbhelper::cancelCommandExecution( + uno::Any( lang::IllegalArgumentException( + "Unknown transfer operation!", + rContext.xProcessor, + -1 ) ), + rContext.xOrigEnv ); + // Unreachable + } + + uno::Reference< ucb::XContent > xNew; + auto pTypeInfo = std::find_if(std::cbegin(aTypesInfo), std::cend(aTypesInfo), + [&lCompare](const ucb::ContentInfo& rTypeInfo) { return lCompare(rTypeInfo.Attributes); }); + if (pTypeInfo != std::cend(aTypesInfo)) + { + // (3) Create a new, empty object of matched type. + + if ( !xCreator.is() ) + { + // First, try it using "CreatabeleContentsInfo" property and + // "createNewContent" command -> the "new" way. + ucb::Command aCreateNewCommand( + "createNewContent", + -1, + uno::Any( *pTypeInfo ) ); + + xCommandProcessorT->execute( aCreateNewCommand, 0, rContext.xEnv ) + >>= xNew; + } + else + { + // Second, try it using XContentCreator interface -> the "old" + // way (not providing the chance to supply an XCommandEnvironment. + + xNew = xCreator->createNewContent( *pTypeInfo ); + } + + if ( !xNew.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "createNewContent failed!", + rContext.xProcessor ); + // Unreachable + } + } + + return xNew; +} + +/// @throws uno::Exception +void transferProperties( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorN ) +{ + ucb::Command aGetPropertySetInfoCommand( + "getPropertySetInfo", + -1, + uno::Any() ); + + uno::Reference< beans::XPropertySetInfo > xInfo; + xCommandProcessorS->execute( aGetPropertySetInfoCommand, 0, rContext.xEnv ) + >>= xInfo; + + if ( !xInfo.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get propertyset info from source object!", + rContext.xProcessor ); + // Unreachable + } + + uno::Sequence< beans::Property > aAllProps = xInfo->getProperties(); + + ucb::Command aGetPropsCommand1( + "getPropertyValues", + -1, + uno::Any( aAllProps ) ); + + uno::Reference< sdbc::XRow > xRow1; + xCommandProcessorS->execute( + aGetPropsCommand1, 0, rContext.xEnv ) >>= xRow1; + + if ( !xRow1.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get properties from source object!", + rContext.xProcessor ); + // Unreachable + } + + // Assemble data structure for setPropertyValues command. + + // Note: Make room for additional Title and TargetURL too. -> + 2 + uno::Sequence< beans::PropertyValue > aPropValues( + aAllProps.getLength() + 2 ); + auto pPropValues = aPropValues.getArray(); + + bool bHasTitle = rContext.aArg.NewTitle.isEmpty(); + bool bHasTargetURL = ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ); + + sal_Int32 nWritePos = 0; + for ( sal_Int32 m = 0; m < aAllProps.getLength(); ++m ) + { + const beans::Property & rCurrProp = aAllProps[ m ]; + beans::PropertyValue & rCurrValue = pPropValues[ nWritePos ]; + + uno::Any aValue; + + if ( rCurrProp.Name == "Title" ) + { + // Supply new title, if given. + if ( !bHasTitle ) + { + bHasTitle = true; + aValue <<= rContext.aArg.NewTitle; + } + } + else if ( rCurrProp.Name == "TargetURL" ) + { + // Supply source URL as link target for the new link to create. + if ( !bHasTargetURL ) + { + bHasTargetURL = true; + aValue <<= rContext.aArg.SourceURL; + } + } + + if ( !aValue.hasValue() ) + { + try + { + aValue = xRow1->getObject( + m + 1, uno::Reference< container::XNameAccess >() ); + } + catch ( sdbc::SQLException const & ) + { + // Argh! But try to bring things to an end. Perhaps the + // mad property is not really important... + } + } + + if ( aValue.hasValue() ) + { + rCurrValue.Name = rCurrProp.Name; + rCurrValue.Handle = rCurrProp.Handle; + rCurrValue.Value = aValue; +// rCurrValue.State = + + nWritePos++; + } + } + + // Title needed, but not set yet? + if ( !bHasTitle && !rContext.aArg.NewTitle.isEmpty() ) + { + pPropValues[ nWritePos ].Name = "Title"; + pPropValues[ nWritePos ].Handle = -1; + pPropValues[ nWritePos ].Value <<= rContext.aArg.NewTitle; + + nWritePos++; + } + + // TargetURL needed, but not set yet? + if ( !bHasTargetURL && ( rContext.aArg.Operation + == ucb::TransferCommandOperation_LINK ) ) + { + pPropValues[ nWritePos ].Name = "TargetURL"; + pPropValues[ nWritePos ].Handle = -1; + pPropValues[ nWritePos ].Value <<= rContext.aArg.SourceURL; + + nWritePos++; + } + + aPropValues.realloc( nWritePos ); + + // Set properties at new object. + + ucb::Command aSetPropsCommand( + "setPropertyValues", + -1, + uno::Any( aPropValues ) ); + + xCommandProcessorN->execute( aSetPropsCommand, 0, rContext.xEnv ); + + // @@@ What to do with source props that are not supported by the + // new object? addProperty ??? +} + +/// @throws uno::Exception +uno::Reference< io::XInputStream > getInputStream( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) +{ + uno::Reference< io::XInputStream > xInputStream; + + + // (1) Try to get data as XInputStream via XActiveDataSink. + + + try + { + uno::Reference< io::XActiveDataSink > xSink = new ActiveDataSink; + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xSink; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); // unused + + ucb::Command aOpenCommand( + "open", + -1, + uno::Any( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + xInputStream = xSink->getInputStream(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + // will be handled below. + } + + if ( !xInputStream.is() ) + { + + + // (2) Try to get data via XOutputStream. + + + try + { + uno::Reference< io::XOutputStream > xOutputStream( io::Pipe::create(rContext.m_xContext), uno::UNO_QUERY_THROW ); + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::DOCUMENT; + aArg.Priority = 0; // unused + aArg.Sink = xOutputStream; + aArg.Properties = uno::Sequence< beans::Property >( 0 ); + + ucb::Command aOpenCommand( + "open", + -1, + uno::Any( aArg ) ); + + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ); + + xInputStream.set( xOutputStream, uno::UNO_QUERY ); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "unable to get input stream from document!" ); + } + } + + return xInputStream; +} + +/// @throws uno::Exception +uno::Reference< sdbc::XResultSet > getResultSet( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XCommandProcessor > & xCommandProcessorS ) +{ + uno::Reference< sdbc::XResultSet > xResultSet; + + uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */), + makeProperty("IsDocument", -1 /* unknown */), + makeProperty("TargetURL", -1 /* unknown */) }; + + ucb::OpenCommandArgument2 aArg; + aArg.Mode = ucb::OpenMode::ALL; + aArg.Priority = 0; // unused + aArg.Sink = nullptr; + aArg.Properties = aProps; + + ucb::Command aOpenCommand( "open", + -1, + uno::Any( aArg ) ); + try + { + uno::Reference< ucb::XDynamicResultSet > xSet; + xCommandProcessorS->execute( aOpenCommand, 0, rContext.xEnv ) >>= xSet; + + if ( xSet.is() ) + xResultSet = xSet->getStaticResultSet(); + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "unable to get result set from folder!" ); + } + + return xResultSet; +} + +/// @throws uno::Exception +void handleNameClashRename( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xNew, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorN, + const uno::Reference< + ucb::XCommandProcessor > & xCommandProcessorS, + /* [inout] */ uno::Reference< io::XInputStream > & xInputStream ) +{ + sal_Int32 nTry = 0; + + // Obtain old title. + uno::Sequence< beans::Property > aProps{ makeProperty("Title", -1) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessorN->execute( aGetPropsCommand, 0, rContext.xEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get properties from new object!", + rContext.xProcessor ); + // Unreachable + } + + OUString aOldTitle = xRow->getString( 1 ); + if ( aOldTitle.isEmpty() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'Title' from new object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // Some pseudo-intelligence for not destroying file extensions. + OUString aOldTitlePre; + OUString aOldTitlePost; + sal_Int32 nPos = aOldTitle.lastIndexOf( '.' ); + if ( nPos != -1 ) + { + aOldTitlePre = aOldTitle.copy( 0, nPos ); + aOldTitlePost = aOldTitle.copy( nPos ); + } + else + aOldTitlePre = aOldTitle; + + if ( nPos > 0 ) + aOldTitlePre += "_"; + + bool bContinue = true; + do + { + nTry++; + + OUString aNewTitle = aOldTitlePre + OUString::number( nTry ) + + aOldTitlePost; + + // Set new title + setTitle( xCommandProcessorN, rContext.xEnv, aNewTitle ); + + // Retry inserting the content. + try + { + // Previous try may have read from stream. Seek to begin (if + // optional interface XSeekable is supported) or get a new stream. + if ( xInputStream.is() ) + { + uno::Reference< io::XSeekable > xSeekable( + xInputStream, uno::UNO_QUERY ); + if ( xSeekable.is() ) + { + try + { + xSeekable->seek( 0 ); + } + catch ( lang::IllegalArgumentException const & ) + { + xInputStream.clear(); + } + catch ( io::IOException const & ) + { + xInputStream.clear(); + } + } + else + xInputStream.clear(); + + if ( !xInputStream.is() ) + { + xInputStream + = getInputStream( rContext, xCommandProcessorS ); + if ( !xInputStream.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(xNew->getIdentifier()->getContentIdentifier())} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Got no data stream from source!", + rContext.xProcessor ); + // Unreachable + } + } + } + + ucb::InsertCommandArgument2 aArg; + aArg.Data = xInputStream; + aArg.ReplaceExisting = false; + + ucb::Command aInsertCommand( + "insert", + -1, + uno::Any( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + + // Success! + bContinue = false; + } + catch ( uno::RuntimeException const & ) + { + throw; + } + catch ( uno::Exception const & ) + { + } + } + while ( bContinue && ( nTry < 50 ) ); + + if ( nTry == 50 ) + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "Unable to resolve name clash!", + rContext.xProcessor, + ucb::NameClash::RENAME ) ), + rContext.xOrigEnv ); + // Unreachable + } +} + +/// @throws uno::Exception +void globalTransfer_( + const TransferCommandContext & rContext, + const uno::Reference< ucb::XContent > & xSource, + const uno::Reference< ucb::XContent > & xTarget, + const uno::Reference< sdbc::XRow > & xSourceProps ) +{ + // IsFolder: property is required. + bool bSourceIsFolder = xSourceProps->getBoolean( 1 ); + if ( !bSourceIsFolder && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'IsFolder' from source object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // IsDocument: property is required. + bool bSourceIsDocument = xSourceProps->getBoolean( 2 ); + if ( !bSourceIsDocument && xSourceProps->wasNull() ) + { + ucbhelper::cancelCommandExecution( + uno::Any( beans::UnknownPropertyException( + "Unable to get property 'IsDocument' from source object!", + rContext.xProcessor ) ), + rContext.xOrigEnv ); + // Unreachable + } + + // TargetURL: property is optional. + bool bSourceIsLink = !xSourceProps->getString( 3 ).isEmpty(); + + + // (1) Try to find a matching target type for the source object and + // create a new, empty object of that type. + + + uno::Reference< ucb::XContent > xNew = createNew( rContext, + xTarget, + bSourceIsFolder, + bSourceIsDocument, + bSourceIsLink ); + if ( !xNew.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Folder", uno::Any(rContext.aArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_CREATE, + aArgs, + rContext.xOrigEnv, + "No matching content type at target!", + rContext.xProcessor ); + // Unreachable + } + + + // (2) Transfer property values from source to new object. + + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorN( + xNew, uno::UNO_QUERY ); + if ( !xCommandProcessorN.is() ) + { + uno::Any aProps(beans::PropertyValue( + "Uri", + -1, + uno::Any( + xNew->getIdentifier()-> + getContentIdentifier()), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_WRITE, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + "New content is not a XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + // Obtain all properties from source. + + uno::Reference< ucb::XCommandProcessor > xCommandProcessorS( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessorS.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Source content is not a XCommandProcessor!", + rContext.xProcessor ); + // Unreachable + } + + transferProperties( rContext, xCommandProcessorS, xCommandProcessorN ); + + + // (3) Try to obtain a data stream from source. + + + uno::Reference< io::XInputStream > xInputStream; + + if ( bSourceIsDocument && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xInputStream = getInputStream( rContext, xCommandProcessorS ); + + + // (4) Try to obtain a resultset (children) from source. + + + uno::Reference< sdbc::XResultSet > xResultSet; + + if ( bSourceIsFolder && ( rContext.aArg.Operation + != ucb::TransferCommandOperation_LINK ) ) + xResultSet = getResultSet( rContext, xCommandProcessorS ); + + + // (5) Insert (store) new content. + + + ucb::InsertCommandArgument2 aArg; + aArg.Data = xInputStream; + aArg.MimeType = rContext.aArg.MimeType; + aArg.DocumentId = rContext.aArg.DocumentId; + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + aArg.ReplaceExisting = true; + break; + + case ucb::NameClash::ERROR: + case ucb::NameClash::RENAME: + case ucb::NameClash::KEEP: // deprecated + case ucb::NameClash::ASK: + aArg.ReplaceExisting = false; + break; + + default: + aArg.ReplaceExisting = false; + OSL_FAIL( "Unknown nameclash directive!" ); + break; + } + + OUString aDesiredName = createDesiredName( rContext.aArg ); + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aInsertCommand( + "insert", + -1, + uno::Any( aArg ) ); + + xCommandProcessorN->execute( aInsertCommand, 0, rContext.xEnv ); + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( !aArg.ReplaceExisting, + "BUG: UnsupportedNameClashException not allowed here!" ); + + if (exc.NameClash != ucb::NameClash::ERROR) { + OSL_FAIL( "BUG: NameClash::ERROR expected!" ); + } + + // No chance to solve name clashes, because I'm not able to detect + // whether there is one. + throw ucb::UnsupportedNameClashException( + "Unable to resolve name clashes, no chance to detect " + "that there is one!", + rContext.xProcessor, + rContext.aArg.NameClash ); + } + catch ( ucb::NameClashException const & ) + { + // The 'insert' command throws a NameClashException if the parameter + // ReplaceExisting of the command's argument was set to false and + // there exists a resource with a clashing name in the target folder + // of the operation. + + // 'insert' command has no direct support for name clashes other + // than ERROR ( ReplaceExisting == false ) and OVERWRITE + // ( ReplaceExisting == true ). So we have to implement the + // other name clash handling directives on top of the content. + + // @@@ 'insert' command should be extended that it accepts a + // name clash handling directive, exactly like 'transfer' command. + + switch ( rContext.aArg.NameClash ) + { + case ucb::NameClash::OVERWRITE: + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "BUG: insert + replace == true MUST NOT " + "throw NameClashException.", + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + [[fallthrough]]; // Unreachable + } + + case ucb::NameClash::ERROR: + throw; + + case ucb::NameClash::RENAME: + { + // "invent" a new valid title. + handleNameClashRename( rContext, + xNew, + xCommandProcessorN, + xCommandProcessorS, + xInputStream ); + break; + } + + case ucb::NameClash::ASK: + { + uno::Any aExc; + OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + rContext.xOrigEnv, // always use original environment! + rContext.aArg.TargetURL, // target folder URL + aDesiredName, + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); + [[fallthrough]]; // break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall through intended. + + case ABORT: + throw ucb::CommandFailedException( + "abort requested via interaction " + "handler", + uno::Reference< uno::XInterface >(), + aExc ); + // break; + + case OVERWRITE: + OSL_ENSURE( !aArg.ReplaceExisting, + "Hu? ReplaceExisting already true?" + ); + aArg.ReplaceExisting = true; + bRetry = true; + break; + + case NEW_NAME: + { + // set new name -> set "Title" property... + if ( setTitle( xCommandProcessorN, + rContext.xEnv, + aNewTitle ) ) + { + // remember suggested title... + aDesiredName = aNewTitle; + + // ... and try again. + bRetry = true; + } + else + { + // error setting title. Abort. + throw ucb::CommandFailedException( + "error setting Title property!", + uno::Reference< uno::XInterface >(), + aExc ); + } + break; + } + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + break; + + case ucb::NameClash::KEEP: // deprecated + default: + { + ucbhelper::cancelCommandExecution( + uno::Any( + ucb::UnsupportedNameClashException( + "default action, don't know how to " + "handle name clash", + rContext.xProcessor, + rContext.aArg.NameClash ) ), + rContext.xOrigEnv ); + // Unreachable + } + } + } + } + while ( bRetry ); + + + // (6) Process children of source. + + + if ( xResultSet.is() ) + { + try + { + // Iterate over children... + + uno::Reference< sdbc::XRow > xChildRow( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildRow.is() ) + { + uno::Any aProps( + beans::PropertyValue( + "Uri", + -1, + uno::Any(rContext.aArg.SourceURL), + beans::PropertyState_DIRECT_VALUE)); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + uno::Sequence< uno::Any >(&aProps, 1), + rContext.xOrigEnv, + "Unable to get properties from children of source!", + rContext.xProcessor ); + // Unreachable + } + + uno::Reference< ucb::XContentAccess > xChildAccess( + xResultSet, uno::UNO_QUERY ); + + if ( !xChildAccess.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rContext.aArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + rContext.xOrigEnv, + "Unable to get children of source!", + rContext.xProcessor ); + // Unreachable + } + + if ( xResultSet->first() ) + { + ucb::GlobalTransferCommandArgument2 aTransArg( + rContext.aArg.Operation, + OUString(), // SourceURL; filled later + xNew->getIdentifier() + ->getContentIdentifier(), // TargetURL + OUString(), // NewTitle; + rContext.aArg.NameClash, + rContext.aArg.MimeType, + rContext.aArg.DocumentId); + + TransferCommandContext aSubCtx( + rContext.m_xContext, + rContext.xProcessor, + rContext.xEnv, + rContext.xOrigEnv, + aTransArg ); + do + { + uno::Reference< ucb::XContent > xChild + = xChildAccess->queryContent(); + if ( xChild.is() ) + { + // Recursion! + + aSubCtx.aArg.SourceURL + = xChild->getIdentifier()->getContentIdentifier(); + + globalTransfer_( aSubCtx, + xChild, + xNew, + xChildRow ); + } + } + while ( xResultSet->next() ); + } + } + catch ( sdbc::SQLException const & ) + { + } + } + + try { + uno::Reference< ucb::XCommandProcessor > xcp( + xTarget, uno::UNO_QUERY ); + + uno::Any aAny; + uno::Reference< ucb::XCommandInfo > xci; + if(xcp.is()) + aAny = + xcp->execute( + ucb::Command( + "getCommandInfo", + -1, + uno::Any()), + 0, + rContext.xEnv ); + + static const OUStringLiteral cmdName(u"flush"); + if((aAny >>= xci) && xci->hasCommandByName(cmdName)) + xcp->execute( + ucb::Command( + cmdName, + -1, + uno::Any()) , + 0, + rContext.xEnv ); + } + catch( uno::Exception const & ) + { + } +} + +} /* namespace */ + + +// UniversalContentBroker implementation ( XCommandProcessor commands ). + + +uno::Reference< ucb::XCommandInfo > +UniversalContentBroker::getCommandInfo() +{ + return uno::Reference< ucb::XCommandInfo >( new CommandProcessorInfo() ); +} + + +void UniversalContentBroker::globalTransfer( + const ucb::GlobalTransferCommandArgument2 & rArg, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) +{ + // Use own command environment with own interaction handler intercepting + // some interaction requests that shall not be handled by the user-supplied + // interaction handler. + uno::Reference< ucb::XCommandEnvironment > xLocalEnv; + if (xEnv.is()) + { + xLocalEnv.set( ucb::CommandEnvironment::create( + m_xContext, + new InteractionHandlerProxy( xEnv->getInteractionHandler() ), + xEnv->getProgressHandler() ) ); + } + + + // (1) Try to transfer the content using 'transfer' command. + + + uno::Reference< ucb::XContent > xTarget; + uno::Reference< ucb::XContentIdentifier > xId + = createContentIdentifier( rArg.TargetURL ); + if ( xId.is() ) + { + try + { + xTarget = queryContent( xId ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + + if ( !xTarget.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate target object!", + this ); + // Unreachable + } + + if ( ( rArg.Operation == ucb::TransferCommandOperation_COPY ) || + ( rArg.Operation == ucb::TransferCommandOperation_MOVE ) ) + { + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Target content is not a XCommandProcessor!", + this ); + // Unreachable + } + + ucb::TransferInfo2 aTransferArg( + ( rArg.Operation + == ucb::TransferCommandOperation_MOVE ), // MoveData + rArg.SourceURL, + rArg.NewTitle, + rArg.NameClash, + rArg.MimeType ); + + bool bRetry; + do + { + bRetry = false; + + try + { + ucb::Command aCommand( + "transfer", // Name + -1, // Handle + uno::Any( aTransferArg ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::InteractiveBadTransferURLException const & ) + { + // Source URL is not supported by target. Try to transfer + // the content "manually". + } + catch ( ucb::UnsupportedCommandException const & ) + { + // 'transfer' command is not supported by commandprocessor. + // Try to transfer manually. + } + catch ( ucb::UnsupportedNameClashException const & exc ) + { + OSL_ENSURE( aTransferArg.NameClash == exc.NameClash, + "nameclash mismatch!" ); + if ( exc.NameClash == ucb::NameClash::ASK ) + { + // Try to detect a name clash by invoking "transfer" with + // NameClash::ERROR. + try + { + ucb::TransferInfo2 aTransferArg1( + aTransferArg.MoveData, + aTransferArg.SourceURL, + aTransferArg.NewTitle, + ucb::NameClash::ERROR, + aTransferArg.MimeType ); + + ucb::Command aCommand1( + "transfer", + -1, + uno::Any( aTransferArg1 ) ); + + xCommandProcessor->execute( aCommand1, 0, xLocalEnv ); + + // Command succeeded. We're done. + return; + } + catch ( ucb::UnsupportedNameClashException const & ) + { + // No chance to solve name clashes, because I'm not + // able to detect whether there is one. + throw exc; // Not just 'throw;'! + } + catch ( ucb::NameClashException const & ) + { + // There's a clash. Use interaction handler to solve it. + + uno::Any aExc; + OUString aNewTitle; + NameClashContinuation eCont + = interactiveNameClashResolve( + xEnv, // always use original environment! + rArg.TargetURL, // target folder URL + createDesiredName( + aTransferArg ), // clashing name + aExc, + aNewTitle ); + + switch ( eCont ) + { + case NOT_HANDLED: + // Not handled. + cppu::throwException( aExc ); + [[fallthrough]]; // break; + + case UNKNOWN: + // Handled, but not clear, how... + // fall through intended. + + case ABORT: + throw ucb::CommandFailedException( + "abort requested via interaction " + "handler", + uno::Reference< uno::XInterface >(), + aExc ); +// break; + + case OVERWRITE: + aTransferArg.NameClash + = ucb::NameClash::OVERWRITE; + bRetry = true; + break; + + case NEW_NAME: + aTransferArg.NewTitle = aNewTitle; + bRetry = true; + break; + } + + OSL_ENSURE( bRetry, "bRetry must be true here!!!" ); + } + } + else + { + throw; + } + } + } + while ( bRetry ); + } + + + // (2) Try to transfer the content "manually". + + + uno::Reference< ucb::XContent > xSource; + try + { + uno::Reference< ucb::XContentIdentifier > xId2 + = createContentIdentifier( rArg.SourceURL ); + if ( xId2.is() ) + xSource = queryContent( xId2 ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + // Error handling via "if ( !xSource.is() )" below. + } + + if ( !xSource.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate source object!", + this ); + // Unreachable + } + + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xSource, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Source content is not a XCommandProcessor!", + this ); + // Unreachable + } + + // Obtain interesting property values from source... + + uno::Sequence< beans::Property > aProps{ makeProperty("IsFolder", -1 /* unknown */), + makeProperty("IsDocument", -1 /* unknown */), + makeProperty("TargetURL", -1 /* unknown */), + makeProperty("BaseURI", -1 /* unknown */) }; + + ucb::Command aGetPropsCommand( + "getPropertyValues", + -1, + uno::Any( aProps ) ); + + uno::Reference< sdbc::XRow > xRow; + xCommandProcessor->execute( aGetPropsCommand, 0, xLocalEnv ) >>= xRow; + + if ( !xRow.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.SourceURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Unable to get properties from source object!", + this ); + // Unreachable + } + + TransferCommandContext aTransferCtx( + m_xContext, this, xLocalEnv, xEnv, rArg ); + + if ( rArg.NewTitle.isEmpty() ) + { + // BaseURI: property is optional. + OUString aBaseURI( xRow->getString( 4 ) ); + if ( !aBaseURI.isEmpty() ) + { + aTransferCtx.aArg.NewTitle + = createDesiredName( aBaseURI, OUString() ); + } + } + + // Do it! + globalTransfer_( aTransferCtx, xSource, xTarget, xRow ); + + + // (3) Delete source, if operation is MOVE. + + + if ( rArg.Operation != ucb::TransferCommandOperation_MOVE ) + return; + + try + { + ucb::Command aCommand( + "delete", // Name + -1, // Handle + uno::Any( true ) ); // Argument + + xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + } + catch ( uno::Exception const & ) + { + OSL_FAIL( "Cannot delete source object!" ); + throw; + } +} + +uno::Any UniversalContentBroker::checkIn( const ucb::CheckinArgument& rArg, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) +{ + uno::Any aRet; + // Use own command environment with own interaction handler intercepting + // some interaction requests that shall not be handled by the user-supplied + // interaction handler. + uno::Reference< ucb::XCommandEnvironment > xLocalEnv; + if (xEnv.is()) + { + xLocalEnv.set( ucb::CommandEnvironment::create( + m_xContext, + new InteractionHandlerProxy( xEnv->getInteractionHandler() ), + xEnv->getProgressHandler() ) ); + } + + uno::Reference< ucb::XContent > xTarget; + uno::Reference< ucb::XContentIdentifier > xId + = createContentIdentifier( rArg.TargetURL ); + if ( xId.is() ) + { + try + { + xTarget = queryContent( xId ); + } + catch ( ucb::IllegalIdentifierException const & ) + { + } + } + + if ( !xTarget.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Can't instantiate target object!", + this ); + // Unreachable + } + + uno::Reference< ucb::XCommandProcessor > xCommandProcessor( + xTarget, uno::UNO_QUERY ); + if ( !xCommandProcessor.is() ) + { + uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence( + { + {"Uri", uno::Any(rArg.TargetURL)} + })); + ucbhelper::cancelCommandExecution( + ucb::IOErrorCode_CANT_READ, + aArgs, + xEnv, + "Target content is not a XCommandProcessor!", + this ); + // Unreachable + } + + try + { + ucb::Command aCommand( + "checkin", -1, + uno::Any( rArg ) ); + + aRet = xCommandProcessor->execute( aCommand, 0, xLocalEnv ); + } + catch ( ucb::UnsupportedCommandException const & ) + { + // 'checkin' command is not supported by commandprocessor: + // ignore. + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucbcmds.hxx b/ucb/source/core/ucbcmds.hxx new file mode 100644 index 000000000..063c79e5f --- /dev/null +++ b/ucb/source/core/ucbcmds.hxx @@ -0,0 +1,36 @@ +/* -*- 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 <rtl/ustring.hxx> + +// Definitions for the commands supported by the UCB. + + +inline constexpr OUStringLiteral GETCOMMANDINFO_NAME = u"getCommandInfo"; +#define GETCOMMANDINFO_HANDLE 1024 + +inline constexpr OUStringLiteral GLOBALTRANSFER_NAME = u"globalTransfer"; +#define GLOBALTRANSFER_HANDLE 1025 + +inline constexpr OUStringLiteral CHECKIN_NAME = u"checkin"; +#define CHECKIN_HANDLE 1026 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucbprops.cxx b/ucb/source/core/ucbprops.cxx new file mode 100644 index 000000000..808923ada --- /dev/null +++ b/ucb/source/core/ucbprops.cxx @@ -0,0 +1,268 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/ucb/CrossReference.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <com/sun/star/ucb/DocumentHeaderField.hpp> +#include <com/sun/star/ucb/DocumentStoreMode.hpp> +#include <com/sun/star/ucb/Priority.hpp> +#include <com/sun/star/ucb/RecipientInfo.hpp> +#include <com/sun/star/ucb/RuleSet.hpp> +#include <com/sun/star/ucb/SendInfo.hpp> +#include <com/sun/star/ucb/SendMediaTypes.hpp> +#include <com/sun/star/ucb/SynchronizePolicy.hpp> +#include <com/sun/star/ucb/VerificationMode.hpp> +#include <com/sun/star/ucb/XDataContainer.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> + +#include "ucbprops.hxx" + +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; + +#define ATTR_DEFAULT ( PropertyAttribute::BOUND | PropertyAttribute::MAYBEVOID | PropertyAttribute::MAYBEDEFAULT ) + +UcbPropertiesManager::UcbPropertiesManager() +: m_pProps({ + { "Account", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "AutoUpdateInterval", -1, cppu::UnoType<sal_Int32>::get(), ATTR_DEFAULT }, + { "ConfirmEmpty", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "ConnectionLimit", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "ConnectionMode", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "ContentCountLimit", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "ContentType", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Cookie", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "CrossReferences", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::CrossReference>>::get(), + ATTR_DEFAULT }, + { "DateCreated", -1, cppu::UnoType<css::util::DateTime>::get(), + ATTR_DEFAULT }, + { "DateModified", -1, cppu::UnoType<css::util::DateTime>::get(), + ATTR_DEFAULT }, + { "DeleteOnServer", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "DocumentBody", -1, cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), + ATTR_DEFAULT }, + { "DocumentCount", -1, cppu::UnoType<sal_Int32>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "DocumentCountMarked", -1, cppu::UnoType<sal_Int32>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "DocumentHeader", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::DocumentHeaderField>>::get(), + ATTR_DEFAULT }, + { "DocumentStoreMode", -1, + cppu::UnoType<css::ucb::DocumentStoreMode>::get(), ATTR_DEFAULT }, + { "DocumentViewMode", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "FTPAccount", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Flags", -1, cppu::UnoType<sal_Int32>::get(), ATTR_DEFAULT }, + { "FolderCount", -1, cppu::UnoType<sal_Int32>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "FolderViewMode", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "FreeSpace", -1, cppu::UnoType<sal_Int64>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "HasDocuments", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "HasFolders", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "IsAutoDelete", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsAutoUpdate", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsDocument", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "IsFolder", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "IsKeepExpired", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsLimitedContentCount", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsMarked", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsRead", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsReadOnly", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsSubscribed", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "IsTimeLimitedStore", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "Keywords", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "LocalBase", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageBCC", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageBody", -1, cppu::UnoType<css::ucb::XDataContainer>::get(), + ATTR_DEFAULT }, + { "MessageCC", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageFrom", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageId", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageInReplyTo", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageReplyTo", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "MessageTo", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "NewsGroups", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "NoCacheList", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Origin", -1, cppu::UnoType<OUString>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "OutgoingMessageRecipients", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::RecipientInfo>>::get(), + ATTR_DEFAULT }, + { "OutgoingMessageState", -1, + cppu::UnoType<css::ucb::OutgoingMessageState>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "OutgoingMessageViewMode", -1, cppu::UnoType<sal_Int16>::get(), + ATTR_DEFAULT }, + { "Password", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Priority", -1, cppu::UnoType<css::ucb::Priority>::get(), ATTR_DEFAULT }, + { "References", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Referer", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "Rules", -1, cppu::UnoType<css::ucb::RuleSet>::get(), ATTR_DEFAULT }, + { "SearchCriteria", -1, cppu::UnoType<css::ucb::RuleSet>::get(), + ATTR_DEFAULT }, + { "SearchIndirections", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "SearchLocations", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "SearchRecursive", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "SeenCount", -1, cppu::UnoType<sal_Int32>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "SendCopyTarget", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendFormats", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendMediaTypes>>::get(), + ATTR_DEFAULT }, + { "SendFroms", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendPasswords", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendProtocolPrivate", -1, cppu::UnoType<sal_Int16>::get(), + ATTR_DEFAULT }, + { "SendProtocolPublic", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "SendReplyTos", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendServerNames", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendUserNames", -1, + cppu::UnoType<css::uno::Sequence<css::ucb::SendInfo>>::get(), + ATTR_DEFAULT }, + { "SendVIMPostOfficePath", -1, cppu::UnoType<OUString>::get(), + ATTR_DEFAULT }, + { "ServerBase", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "ServerName", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "ServerPort", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "Size", -1, cppu::UnoType<sal_Int64>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "SizeLimit", -1, cppu::UnoType<sal_Int64>::get(), ATTR_DEFAULT }, + { "SubscribedCount", -1, cppu::UnoType<sal_Int32>::get(), + ATTR_DEFAULT | PropertyAttribute::READONLY }, + { "SynchronizePolicy", -1, + cppu::UnoType<css::ucb::SynchronizePolicy>::get(), ATTR_DEFAULT }, + { "TargetFrames", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "TargetURL", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "TimeLimitStore", -1, cppu::UnoType<sal_Int16>::get(), ATTR_DEFAULT }, + { "Title", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "UpdateOnOpen", -1, cppu::UnoType<bool>::get(), ATTR_DEFAULT }, + { "UseOutBoxPrivateProtocolSettings", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT }, + { "UseOutBoxPublicProtocolSettings", -1, cppu::UnoType<bool>::get(), + ATTR_DEFAULT }, + { "UserName", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "UserSortCriterium", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "VIMPostOfficePath", -1, cppu::UnoType<OUString>::get(), ATTR_DEFAULT }, + { "VerificationMode", -1, cppu::UnoType<css::ucb::VerificationMode>::get(), + ATTR_DEFAULT }}) +{ +} + + +// virtual +UcbPropertiesManager::~UcbPropertiesManager() +{ +} + +// XServiceInfo methods. + +OUString SAL_CALL UcbPropertiesManager::getImplementationName() +{ + return "com.sun.star.comp.ucb.UcbPropertiesManager"; +} +sal_Bool SAL_CALL UcbPropertiesManager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} +css::uno::Sequence< OUString > SAL_CALL UcbPropertiesManager::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.PropertiesManager" }; +} + + + +// Service factory implementation. + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UcbPropertiesManager_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UcbPropertiesManager()); +} + +// XPropertySetInfo methods. + + +// virtual +Sequence< Property > SAL_CALL UcbPropertiesManager::getProperties() +{ + return m_pProps; +} + + +// virtual +Property SAL_CALL UcbPropertiesManager::getPropertyByName( const OUString& aName ) +{ + Property aProp; + if ( queryProperty( aName, aProp ) ) + return aProp; + + throw UnknownPropertyException(aName); +} + + +// virtual +sal_Bool SAL_CALL UcbPropertiesManager::hasPropertyByName( const OUString& Name ) +{ + Property aProp; + return queryProperty( Name, aProp ); +} + + +// Non-Interface methods. + + +bool UcbPropertiesManager::queryProperty( + const OUString& rName, Property& rProp ) +{ + auto pProp = std::find_if(std::cbegin(m_pProps), std::cend(m_pProps), + [&rName](const Property& rCurrProp) { return rCurrProp.Name == rName; }); + if (pProp != std::cend(m_pProps)) + { + rProp = *pProp; + return true; + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucbprops.hxx b/ucb/source/core/ucbprops.hxx new file mode 100644 index 000000000..a78222317 --- /dev/null +++ b/ucb/source/core/ucbprops.hxx @@ -0,0 +1,56 @@ +/* -*- 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 <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <cppuhelper/implbase.hxx> + +using UcbPropertiesManager_Base = cppu::WeakImplHelper < + css::lang::XServiceInfo, + css::beans::XPropertySetInfo >; + +class UcbPropertiesManager : public UcbPropertiesManager_Base +{ + css::uno::Sequence< css::beans::Property > m_pProps; + +private: + bool queryProperty( const OUString& rName, + css::beans::Property& rProp ); + +public: + explicit UcbPropertiesManager(); + virtual ~UcbPropertiesManager() override; + + // 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; + + // XPropertySetInfo + virtual css::uno::Sequence< css::beans::Property > SAL_CALL + getProperties() override; + virtual css::beans::Property SAL_CALL + getPropertyByName( const OUString& aName ) override; + virtual sal_Bool SAL_CALL + hasPropertyByName( const OUString& Name ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucbstore.cxx b/ucb/source/core/ucbstore.cxx new file mode 100644 index 000000000..01f7c70b5 --- /dev/null +++ b/ucb/source/core/ucbstore.cxx @@ -0,0 +1,2205 @@ +/* -*- 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 + ************************************************************************** + + *************************************************************************/ + +#include <optional> +#include <unordered_map> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/ref.hxx> +#include <comphelper/interfacecontainer2.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/beans/IllegalTypeException.hpp> +#include <com/sun/star/beans/NotRemoveableException.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyExistException.hpp> +#include <com/sun/star/beans/PropertySetInfoChange.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XNameReplace.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weak.hxx> +#include "ucbstore.hxx" + +using namespace com::sun::star::beans; +using namespace com::sun::star::configuration; +using namespace com::sun::star::container; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace com::sun::star::uno; +using namespace com::sun::star::util; +using namespace comphelper; +using namespace cppu; + +static OUString makeHierarchalNameSegment( const OUString & rIn ) +{ + OUStringBuffer aBuffer; + aBuffer.append( "['" ); + + sal_Int32 nCount = rIn.getLength(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const sal_Unicode c = rIn[ n ]; + switch ( c ) + { + case '&': + aBuffer.append( "&" ); + break; + + case '"': + aBuffer.append( """ ); + break; + + case '\'': + aBuffer.append( "'" ); + break; + + case '<': + aBuffer.append( "<" ); + break; + + case '>': + aBuffer.append( ">" ); + break; + + default: + aBuffer.append( c ); + break; + } + } + + aBuffer.append( "']" ); + return aBuffer.makeStringAndClear(); +} + +constexpr OUStringLiteral STORE_CONTENTPROPERTIES_KEY = u"/org.openoffice.ucb.Store/ContentProperties"; + +// describe path of cfg entry +constexpr OUStringLiteral CFGPROPERTY_NODEPATH = u"nodepath"; + +class PropertySetInfo_Impl : public cppu::WeakImplHelper < XPropertySetInfo > +{ + std::optional<Sequence< Property >> + m_xProps; + PersistentPropertySet* m_pOwner; + +public: + explicit PropertySetInfo_Impl(PersistentPropertySet* pOwner); + + // XPropertySetInfo + virtual Sequence< Property > SAL_CALL getProperties() override; + virtual Property SAL_CALL getPropertyByName( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override; + + // Non-interface methods. + void reset() { m_xProps.reset(); } +}; + + +// UcbStore Implementation. + + +UcbStore::UcbStore( const Reference< XComponentContext >& xContext ) +: m_xContext( xContext ) +{ +} + + +// virtual +UcbStore::~UcbStore() +{ +} + +OUString SAL_CALL UcbStore::getImplementationName() +{ + return "com.sun.star.comp.ucb.UcbStore"; +} +sal_Bool SAL_CALL UcbStore::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} +css::uno::Sequence< OUString > SAL_CALL UcbStore::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.Store" }; +} + +// Service factory implementation. + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UcbStore_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UcbStore(context)); +} + + +// XPropertySetRegistryFactory methods. + + +// virtual +Reference< XPropertySetRegistry > SAL_CALL +UcbStore::createPropertySetRegistry( const OUString& ) +{ + // The URL parameter is ignored by this interface implementation. It always + // uses the configuration server as storage medium. + + if ( !m_xTheRegistry.is() ) + { + std::unique_lock aGuard( m_aMutex ); + if ( !m_xTheRegistry.is() ) + m_xTheRegistry = new PropertySetRegistry( m_xContext, m_aInitArgs ); + } + + return m_xTheRegistry; +} + + +// XInitialization methods. + + +// virtual +void SAL_CALL UcbStore::initialize( const Sequence< Any >& aArguments ) +{ + std::unique_lock aGuard( m_aMutex ); + m_aInitArgs = aArguments; +} + + + +// PropertySetRegistry Implementation. + + +PropertySetRegistry::PropertySetRegistry( + const Reference< XComponentContext >& xContext, + const Sequence< Any > &rInitArgs ) +: m_xContext( xContext ) +, m_aInitArgs(rInitArgs) +, m_bTriedToGetRootReadAccess(false) +, m_bTriedToGetRootWriteAccess(false) +{ +} + + +// virtual +PropertySetRegistry::~PropertySetRegistry() +{ +} + + +// XServiceInfo methods. + + +OUString SAL_CALL PropertySetRegistry::getImplementationName() +{ + return "com.sun.star.comp.ucb.PropertySetRegistry"; +} + +sal_Bool SAL_CALL PropertySetRegistry::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +css::uno::Sequence< OUString > SAL_CALL PropertySetRegistry::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.PropertySetRegistry" }; +} + + +// XPropertySetRegistry methods. + + +// virtual +Reference< XPersistentPropertySet > SAL_CALL +PropertySetRegistry::openPropertySet( const OUString& key, sal_Bool create ) +{ + if ( !key.isEmpty() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + PropertySetMap_Impl& rSets = m_aPropSets; + + PropertySetMap_Impl::const_iterator it = rSets.find( key ); + if ( it != rSets.end() ) + { + // Already instantiated. + return Reference< XPersistentPropertySet >( (*it).second ); + } + else + { + // Create new instance. + Reference< XNameAccess > xRootNameAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootNameAccess.is() ) + { + // Propertyset in registry? + if ( xRootNameAccess->hasByName( key ) ) + { + // Yep! + return Reference< XPersistentPropertySet >( + new PersistentPropertySet( + *this, key ) ); + } + else if ( create ) + { + // No. Create entry for propertyset. + + Reference< XSingleServiceFactory > xFac( + getConfigWriteAccess( OUString() ), UNO_QUERY ); + Reference< XChangesBatch > xBatch( xFac, UNO_QUERY ); + Reference< XNameContainer > xContainer( xFac, UNO_QUERY ); + + OSL_ENSURE( xFac.is(), + "PropertySetRegistry::openPropertySet - " + "No factory!" ); + + OSL_ENSURE( xBatch.is(), + "PropertySetRegistry::openPropertySet - " + "No batch!" ); + + OSL_ENSURE( xContainer.is(), + "PropertySetRegistry::openPropertySet - " + "No container!" ); + + if ( xFac.is() && xBatch.is() && xContainer.is() ) + { + try + { + // Create new "Properties" config item. + Reference< XNameReplace > xNameReplace( + xFac->createInstance(), UNO_QUERY ); + + if ( xNameReplace.is() ) + { + // Fill new item... + + // Insert new item. + xContainer->insertByName( + key, Any( xNameReplace ) ); + // Commit changes. + xBatch->commitChanges(); + + return Reference< XPersistentPropertySet >( + new PersistentPropertySet( + *this, key ) ); + } + } + catch (const IllegalArgumentException&) + { + // insertByName + + OSL_FAIL( "PropertySetRegistry::openPropertySet - " + "caught IllegalArgumentException!" ); + } + catch (const ElementExistException&) + { + // insertByName + + OSL_FAIL( "PropertySetRegistry::openPropertySet - " + "caught ElementExistException!" ); + } + catch (const WrappedTargetException&) + { + // insertByName, commitChanges + + OSL_FAIL( "PropertySetRegistry::openPropertySet - " + "caught WrappedTargetException!" ); + } + catch (const RuntimeException&) + { + OSL_FAIL( "PropertySetRegistry::openPropertySet - " + "caught RuntimeException!" ); + } + catch (const Exception&) + { + // createInstance + + OSL_FAIL( "PropertySetRegistry::openPropertySet - " + "caught Exception!" ); + } + } + } + else + { + // No entry. Fail, but no error. + return Reference< XPersistentPropertySet >(); + } + } + + SAL_WARN( "ucb", "no root access" ); + } + } + + return Reference< XPersistentPropertySet >(); +} + + +// virtual +void SAL_CALL PropertySetRegistry::removePropertySet( const OUString& key ) +{ + if ( key.isEmpty() ) + return; + + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XNameAccess > xRootNameAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootNameAccess.is() ) + { + // Propertyset in registry? + if ( !xRootNameAccess->hasByName( key ) ) + return; + Reference< XChangesBatch > xBatch( + getConfigWriteAccess( OUString() ), UNO_QUERY ); + Reference< XNameContainer > xContainer( xBatch, UNO_QUERY ); + + if ( xBatch.is() && xContainer.is() ) + { + try + { + // Remove item. + xContainer->removeByName( key ); + // Commit changes. + xBatch->commitChanges(); + + // Success. + return; + } + catch (const NoSuchElementException&) + { + // removeByName + + OSL_FAIL( "PropertySetRegistry::removePropertySet - " + "caught NoSuchElementException!" ); + return; + } + catch (const WrappedTargetException&) + { + // commitChanges + + OSL_FAIL( "PropertySetRegistry::removePropertySet - " + "caught WrappedTargetException!" ); + return; + } + } + + return; + } + + SAL_WARN( "ucb", "no root access" ); +} + + +// XElementAccess methods. + + +// virtual +css::uno::Type SAL_CALL PropertySetRegistry::getElementType() +{ + return cppu::UnoType<XPersistentPropertySet>::get(); +} + + +// virtual +sal_Bool SAL_CALL PropertySetRegistry::hasElements() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XElementAccess > xElemAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xElemAccess.is() ) + return xElemAccess->hasElements(); + + return false; +} + + +// XNameAccess methods. + + +// virtual +Any SAL_CALL PropertySetRegistry::getByName( const OUString& aName ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XNameAccess > xNameAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xNameAccess.is() ) + { + + try + { + return xNameAccess->getByName( aName ); + } + catch (const NoSuchElementException&) + { + // getByName + } + catch (const WrappedTargetException&) + { + // getByName + } + } + + return Any(); +} + + +// virtual +Sequence< OUString > SAL_CALL PropertySetRegistry::getElementNames() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XNameAccess > xNameAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xNameAccess.is() ) + { + return xNameAccess->getElementNames(); + } + return Sequence< OUString >( 0 ); +} + + +// virtual +sal_Bool SAL_CALL PropertySetRegistry::hasByName( const OUString& aName ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XNameAccess > xNameAccess( + getRootConfigReadAccess(), UNO_QUERY ); + if ( xNameAccess.is() ) + { + return xNameAccess->hasByName( aName ); + } + + return false; +} + + +void PropertySetRegistry::add( PersistentPropertySet* pSet ) +{ + OUString key( pSet->getKey() ); + + if ( !key.isEmpty() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + m_aPropSets[ key ] = pSet; + } +} + + +void PropertySetRegistry::remove( PersistentPropertySet* pSet ) +{ + OUString key( pSet->getKey() ); + + if ( key.isEmpty() ) + return; + + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + PropertySetMap_Impl& rSets = m_aPropSets; + + PropertySetMap_Impl::iterator it = rSets.find( key ); + if ( it != rSets.end() ) + { + // Found. + rSets.erase( it ); + } +} + + +void PropertySetRegistry::renamePropertySet( const OUString& rOldKey, + const OUString& rNewKey ) +{ + if ( rOldKey == rNewKey ) + return; + + Reference< XNameAccess > xRootNameAccess( + getConfigWriteAccess( OUString() ), UNO_QUERY ); + if ( xRootNameAccess.is() ) + { + // Old key present? + if ( xRootNameAccess->hasByName( rOldKey ) ) + { + // New key not present? + if ( xRootNameAccess->hasByName( rNewKey ) ) + { + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "New key exists!" ); + return; + } + Reference< XSingleServiceFactory > xFac( + xRootNameAccess, UNO_QUERY ); + Reference< XChangesBatch > xBatch( xFac, UNO_QUERY ); + Reference< XNameContainer > xContainer( xFac, UNO_QUERY ); + + OSL_ENSURE( xFac.is(), + "PropertySetRegistry::renamePropertySet - " + "No factory!" ); + + OSL_ENSURE( xBatch.is(), + "PropertySetRegistry::renamePropertySet - " + "No batch!" ); + + OSL_ENSURE( xContainer.is(), + "PropertySetRegistry::renamePropertySet - " + "No container!" ); + + if ( xFac.is() && xBatch.is() && xContainer.is() ) + { + + // Create new "Properties" config item. + + + try + { + Reference< XNameReplace > xNameReplace( + xFac->createInstance(), UNO_QUERY ); + + if ( xNameReplace.is() ) + { + // Insert new item. + xContainer->insertByName( + rNewKey, Any( xNameReplace ) ); + // Commit changes. + xBatch->commitChanges(); + } + } + catch (const IllegalArgumentException&) + { + // insertByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught IllegalArgumentException!" ); + return; + } + catch (const ElementExistException&) + { + // insertByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught ElementExistException!" ); + return; + } + catch (const WrappedTargetException&) + { + // insertByName, commitChanges + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught WrappedTargetException!" ); + return; + } + catch (const RuntimeException&) + { + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught RuntimeException!" ); + return; + } + catch (const Exception&) + { + // createInstance + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught Exception!" ); + return; + } + + + // Copy data... + + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + xRootNameAccess, UNO_QUERY ); + if ( !xRootHierNameAccess.is() ) + { + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "No hierarchical name access!" ); + return; + } + + try + { + OUString aOldValuesKey + = makeHierarchalNameSegment( rOldKey ) + "/Values"; + + Reference< XNameAccess > xOldNameAccess; + xRootHierNameAccess->getByHierarchicalName( + aOldValuesKey ) + >>= xOldNameAccess; + if ( !xOldNameAccess.is() ) + { + OSL_FAIL( "PersistentPropertySet::renamePropertySet - " + "No old name access!" ); + return; + } + + // Obtain property names. + const Sequence< OUString > aElems + = xOldNameAccess->getElementNames(); + if ( aElems.hasElements() ) + { + OUString aNewValuesKey + = makeHierarchalNameSegment( rNewKey ) + "/Values"; + + Reference< XSingleServiceFactory > xNewFac; + xRootHierNameAccess->getByHierarchicalName( + aNewValuesKey ) + >>= xNewFac; + if ( !xNewFac.is() ) + { + OSL_FAIL( "PersistentPropertySet::renamePropertySet - " + "No new factory!" ); + return; + } + + Reference< XNameContainer > xNewContainer( + xNewFac, UNO_QUERY ); + if ( !xNewContainer.is() ) + { + OSL_FAIL( "PersistentPropertySet::renamePropertySet - " + "No new container!" ); + return; + } + + aOldValuesKey += "/"; + + OUString const aHandleKey("/Handle"); + OUString const aValueKey("/Value"); + OUString const aStateKey("/State"); + OUString const aAttrKey("/Attributes"); + + for ( const OUString& rPropName : aElems ) + { + // Create new item. + Reference< XNameReplace > xNewPropNameReplace( + xNewFac->createInstance(), UNO_QUERY ); + + if ( !xNewPropNameReplace.is() ) + { + OSL_FAIL( "PersistentPropertySet::renamePropertySet - " + "No new prop name replace!" ); + return; + } + + // Fill new item... + + // Set Values + OUString aKey = aOldValuesKey; + aKey += makeHierarchalNameSegment( rPropName ); + + // ... handle + OUString aNewKey1 = aKey + aHandleKey; + Any aAny = + xRootHierNameAccess->getByHierarchicalName( + aNewKey1 ); + xNewPropNameReplace->replaceByName( "Handle", aAny ); + + // ... value + aNewKey1 = aKey + aValueKey; + aAny = + xRootHierNameAccess->getByHierarchicalName( + aNewKey1 ); + xNewPropNameReplace->replaceByName( "Value", aAny ); + + // ... state + aNewKey1 = aKey + aStateKey; + aAny = + xRootHierNameAccess->getByHierarchicalName( + aNewKey1 ); + xNewPropNameReplace->replaceByName( "State", aAny ); + + // ... attributes + aNewKey1 = aKey + aAttrKey; + aAny = + xRootHierNameAccess->getByHierarchicalName( + aNewKey1 ); + xNewPropNameReplace->replaceByName( "Attributes", aAny ); + + // Insert new item. + xNewContainer->insertByName( + rPropName, Any( xNewPropNameReplace ) ); + + // Commit changes. + xBatch->commitChanges(); + } + } + } + catch (const IllegalArgumentException&) + { + // insertByName, replaceByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught IllegalArgumentException!" ); + return; + } + catch (const ElementExistException&) + { + // insertByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught ElementExistException!" ); + return; + } + catch (const WrappedTargetException&) + { + // insertByName, replaceByName, commitChanges + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught WrappedTargetException!" ); + return; + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName, replaceByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught NoSuchElementException!" ); + return; + } + catch (const RuntimeException&) + { + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught RuntimeException!" ); + return; + } + catch (const Exception&) + { + // createInstance + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught Exception!" ); + return; + } + + + // Remove old entry... + + + try + { + // Remove item. + xContainer->removeByName( rOldKey ); + // Commit changes. + xBatch->commitChanges(); + + // Success. + return; + } + catch (const NoSuchElementException&) + { + // removeByName + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught NoSuchElementException!" ); + return; + } + catch (const WrappedTargetException&) + { + // commitChanges + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - " + "caught WrappedTargetException!" ); + return; + } + } + } + } + + OSL_FAIL( "PropertySetRegistry::renamePropertySet - Error!" ); +} + + +Reference< XMultiServiceFactory > PropertySetRegistry::getConfigProvider() +{ + if ( !m_xConfigProvider.is() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + if ( !m_xConfigProvider.is() ) + { + const Sequence< Any >& rInitArgs = m_aInitArgs; + + if ( rInitArgs.hasElements() ) + { + // Extract config provider from service init args. + rInitArgs[ 0 ] >>= m_xConfigProvider; + + OSL_ENSURE( m_xConfigProvider.is(), + "PropertySetRegistry::getConfigProvider - " + "No config provider!" ); + } + else + { + try + { + m_xConfigProvider = theDefaultProvider::get( m_xContext ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "ucb", ""); + } + } + } + } + + return m_xConfigProvider; +} + + +Reference< XInterface > PropertySetRegistry::getRootConfigReadAccess() +{ + try + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( !m_xRootReadAccess.is() ) + { + if ( m_bTriedToGetRootReadAccess ) + { + OSL_FAIL( "PropertySetRegistry::getRootConfigReadAccess - " + "Unable to read any config data! -> #82494#" ); + return Reference< XInterface >(); + } + + getConfigProvider(); + + if ( m_xConfigProvider.is() ) + { + Sequence<Any> aArguments(comphelper::InitAnyPropertySequence( + { + {CFGPROPERTY_NODEPATH, Any(OUString( STORE_CONTENTPROPERTIES_KEY ))} + })); + + m_bTriedToGetRootReadAccess = true; + + m_xRootReadAccess = + m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments ); + + if ( m_xRootReadAccess.is() ) + return m_xRootReadAccess; + } + } + else + return m_xRootReadAccess; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + // createInstance, createInstanceWithArguments + + TOOLS_WARN_EXCEPTION("ucb", ""); + return Reference< XInterface >(); + } + + SAL_WARN( "ucb", "Error!" ); + return Reference< XInterface >(); +} + + +Reference< XInterface > PropertySetRegistry::getConfigWriteAccess( + const OUString& rPath ) +{ + try + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( !m_xRootWriteAccess.is() ) + { + if ( m_bTriedToGetRootWriteAccess ) + { + OSL_FAIL( "PropertySetRegistry::getConfigWriteAccess - " + "Unable to write any config data! -> #82494#" ); + return Reference< XInterface >(); + } + + getConfigProvider(); + + if ( m_xConfigProvider.is() ) + { + Sequence<Any> aArguments(comphelper::InitAnyPropertySequence( + { + {CFGPROPERTY_NODEPATH, Any(OUString( STORE_CONTENTPROPERTIES_KEY ))} + })); + + m_bTriedToGetRootWriteAccess = true; + + m_xRootWriteAccess = + m_xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", + aArguments ); + + OSL_ENSURE( m_xRootWriteAccess.is(), + "PropertySetRegistry::getConfigWriteAccess - " + "No config update access!" ); + } + } + + if ( m_xRootWriteAccess.is() ) + { + if ( !rPath.isEmpty() ) + { + Reference< XHierarchicalNameAccess > xNA( + m_xRootWriteAccess, UNO_QUERY ); + if ( xNA.is() ) + { + Reference< XInterface > xInterface; + xNA->getByHierarchicalName( rPath ) >>= xInterface; + + if ( xInterface.is() ) + return xInterface; + } + } + else + return m_xRootWriteAccess; + } + } + catch (const RuntimeException&) + { + throw; + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PropertySetRegistry::getConfigWriteAccess - " + "caught NoSuchElementException!" ); + return Reference< XInterface >(); + } + catch (const Exception&) + { + // createInstance, createInstanceWithArguments + + OSL_FAIL( "PropertySetRegistry::getConfigWriteAccess - " + "caught Exception!" ); + return Reference< XInterface >(); + } + + OSL_FAIL( "PropertySetRegistry::getConfigWriteAccess - Error!" ); + return Reference< XInterface >(); +} + + +// PersistentPropertySet Implementation. + + +PersistentPropertySet::PersistentPropertySet( + PropertySetRegistry& rCreator, + const OUString& rKey ) +: m_pCreator( &rCreator ), m_aKey( rKey ) +{ + // register at creator. + rCreator.add( this ); +} + + +// virtual +PersistentPropertySet::~PersistentPropertySet() +{ + // deregister at creator. + m_pCreator->remove( this ); +} + +// XServiceInfo methods. + +OUString SAL_CALL PersistentPropertySet::getImplementationName() +{ + return "com.sun.star.comp.ucb.PersistentPropertySet"; +} + +sal_Bool SAL_CALL PersistentPropertySet::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +css::uno::Sequence< OUString > SAL_CALL PersistentPropertySet::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.PersistentPropertySet" }; +} + + +// XComponent methods. + + +// virtual +void SAL_CALL PersistentPropertySet::dispose() +{ + if ( m_pDisposeEventListeners && + m_pDisposeEventListeners->getLength() ) + { + EventObject aEvt; + aEvt.Source = static_cast< XComponent * >( this ); + m_pDisposeEventListeners->disposeAndClear( aEvt ); + } + + if ( m_pPropSetChangeListeners && + m_pPropSetChangeListeners->getLength() ) + { + EventObject aEvt; + aEvt.Source = static_cast< XPropertySetInfoChangeNotifier * >( this ); + m_pPropSetChangeListeners->disposeAndClear( aEvt ); + } + + if ( m_pPropertyChangeListeners ) + { + EventObject aEvt; + aEvt.Source = static_cast< XPropertySet * >( this ); + m_pPropertyChangeListeners->disposeAndClear( aEvt ); + } +} + + +// virtual +void SAL_CALL PersistentPropertySet::addEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( !m_pDisposeEventListeners ) + m_pDisposeEventListeners.reset( + new OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutex ) ); + + m_pDisposeEventListeners->addInterface( Listener ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::removeEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( m_pDisposeEventListeners ) + m_pDisposeEventListeners->removeInterface( Listener ); + + // Note: Don't want to delete empty container here -> performance. +} + + +// XPropertySet methods. + + +// virtual +Reference< XPropertySetInfo > SAL_CALL PersistentPropertySet::getPropertySetInfo() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( !m_pInfo.is() ) + { + m_pInfo = new PropertySetInfo_Impl( this ); + } + return m_pInfo; +} + + +// virtual +void SAL_CALL PersistentPropertySet::setPropertyValue( const OUString& aPropertyName, + const Any& aValue ) +{ + osl::ClearableGuard< osl::Mutex > aCGuard( m_aMutex ); + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + OUString aFullPropName( getFullKey() + "/" ); + aFullPropName += makeHierarchalNameSegment( aPropertyName ); + + // Does property exist? + if ( xRootHierNameAccess->hasByHierarchicalName( aFullPropName ) ) + { + Reference< XNameReplace > xNameReplace( + m_pCreator->getConfigWriteAccess( + aFullPropName ), UNO_QUERY ); + Reference< XChangesBatch > xBatch( + m_pCreator->getConfigWriteAccess( + OUString() ), UNO_QUERY ); + + if ( xNameReplace.is() && xBatch.is() ) + { + try + { + // Obtain old value + OUString aValueName = aFullPropName + "/Value"; + Any aOldValue + = xRootHierNameAccess->getByHierarchicalName( + aValueName ); + // Check value type. + if ( aOldValue.getValueType() != aValue.getValueType() ) + { + aCGuard.clear(); + throw IllegalArgumentException(); + } + + // Write value + xNameReplace->replaceByName( "Value", aValue ); + + // Write state ( Now it is a directly set value ) + xNameReplace->replaceByName( + "State", + Any( + sal_Int32( + PropertyState_DIRECT_VALUE ) ) ); + + // Commit changes. + xBatch->commitChanges(); + + PropertyChangeEvent aEvt; + if ( m_pPropertyChangeListeners ) + { + // Obtain handle + aValueName = aFullPropName + "/Handle"; + sal_Int32 nHandle = -1; + xRootHierNameAccess->getByHierarchicalName( aValueName ) + >>= nHandle; + + aEvt.Source = static_cast<OWeakObject*>(this); + aEvt.PropertyName = aPropertyName; + aEvt.PropertyHandle = nHandle; + aEvt.Further = false; + aEvt.OldValue = aOldValue; + aEvt.NewValue = aValue; + + // Callback follows! + aCGuard.clear(); + + notifyPropertyChangeEvent( aEvt ); + } + return; + } + catch (const IllegalArgumentException&) + { + // replaceByName + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName, replaceByName + } + catch (const WrappedTargetException&) + { + // replaceByName, commitChanges + } + } + } + } + + throw UnknownPropertyException(aPropertyName); +} + + +// virtual +Any SAL_CALL PersistentPropertySet::getPropertyValue( + const OUString& PropertyName ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XHierarchicalNameAccess > xNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xNameAccess.is() ) + { + OUString aFullPropName( getFullKey() + "/" ); + aFullPropName += makeHierarchalNameSegment( PropertyName ) + "/Value"; + try + { + return xNameAccess->getByHierarchicalName( aFullPropName ); + } + catch (const NoSuchElementException&) + { + throw UnknownPropertyException(aFullPropName); + } + } + + throw UnknownPropertyException(PropertyName); +} + + +// virtual +void SAL_CALL PersistentPropertySet::addPropertyChangeListener( + const OUString& aPropertyName, + const Reference< XPropertyChangeListener >& xListener ) +{ +// load(); + + if ( !m_pPropertyChangeListeners ) + m_pPropertyChangeListeners.reset( + new PropertyListeners_Impl( m_aMutex ) ); + + m_pPropertyChangeListeners->addInterface( + aPropertyName, xListener ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::removePropertyChangeListener( + const OUString& aPropertyName, + const Reference< XPropertyChangeListener >& aListener ) +{ +// load(); + + if ( m_pPropertyChangeListeners ) + m_pPropertyChangeListeners->removeInterface( + aPropertyName, aListener ); + + // Note: Don't want to delete empty container here -> performance. +} + + +// virtual +void SAL_CALL PersistentPropertySet::addVetoableChangeListener( + const OUString&, + const Reference< XVetoableChangeListener >& ) +{ +// load(); +// OSL_FAIL( // "PersistentPropertySet::addVetoableChangeListener - N.Y.I." ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::removeVetoableChangeListener( + const OUString&, + const Reference< XVetoableChangeListener >& ) +{ +// load(); +// OSL_FAIL( // "PersistentPropertySet::removeVetoableChangeListener - N.Y.I." ); +} + + +// XPersistentPropertySet methods. + + +// virtual +Reference< XPropertySetRegistry > SAL_CALL PersistentPropertySet::getRegistry() +{ + return m_pCreator; +} + + +// virtual +OUString SAL_CALL PersistentPropertySet::getKey() +{ + return m_aKey; +} + + +// XNamed methods. + + +// virtual +OUString SAL_CALL PersistentPropertySet::getName() +{ + // same as getKey() + return m_aKey; +} + + +// virtual +void SAL_CALL PersistentPropertySet::setName( const OUString& aName ) +{ + if ( aName != m_aKey ) + m_pCreator->renamePropertySet( m_aKey, aName ); +} + + +// XPropertyContainer methods. + + +// virtual +void SAL_CALL PersistentPropertySet::addProperty( + const OUString& Name, sal_Int16 Attributes, const Any& DefaultValue ) +{ + if ( Name.isEmpty() ) + throw IllegalArgumentException(); + + // @@@ What other types can't be written to config server? + + // Check type class ( Not all types can be written to storage ) + TypeClass eTypeClass = DefaultValue.getValueTypeClass(); + if ( eTypeClass == TypeClass_INTERFACE ) + throw IllegalTypeException(); + + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + // Property already in set? + + OUString aFullValuesName; + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + aFullValuesName = getFullKey(); + OUString aFullPropName = aFullValuesName + "/"; + aFullPropName += makeHierarchalNameSegment( Name ); + + if ( xRootHierNameAccess->hasByHierarchicalName( aFullPropName ) ) + { + // Already in set. + throw PropertyExistException(); + } + } + + // Property is always removable. + Attributes |= PropertyAttribute::REMOVABLE; + + // Add property. + + Reference< XSingleServiceFactory > xFac( + m_pCreator->getConfigWriteAccess( aFullValuesName ), + UNO_QUERY ); + Reference< XNameContainer > xContainer( xFac, UNO_QUERY ); + Reference< XChangesBatch > xBatch( + m_pCreator->getConfigWriteAccess( OUString() ), + UNO_QUERY ); + + OSL_ENSURE( xFac.is(), + "PersistentPropertySet::addProperty - No factory!" ); + + OSL_ENSURE( xBatch.is(), + "PersistentPropertySet::addProperty - No batch!" ); + + OSL_ENSURE( xContainer.is(), + "PersistentPropertySet::addProperty - No container!" ); + + if ( xFac.is() && xBatch.is() && xContainer.is() ) + { + try + { + // Create new "PropertyValue" config item. + Reference< XNameReplace > xNameReplace( + xFac->createInstance(), UNO_QUERY ); + + if ( xNameReplace.is() ) + { + // Fill new item... + + // Set handle + xNameReplace->replaceByName( + "Handle", + Any( sal_Int32( -1 ) ) ); + + // Set default value + xNameReplace->replaceByName( + "Value", + DefaultValue ); + + // Set state ( always "default" ) + xNameReplace->replaceByName( + "State", + Any( + sal_Int32( + PropertyState_DEFAULT_VALUE ) ) ); + + // Set attributes + xNameReplace->replaceByName( + "Attributes", + Any( sal_Int32( Attributes ) ) ); + + // Insert new item. + xContainer->insertByName( Name, Any( xNameReplace ) ); + + // Commit changes. + xBatch->commitChanges(); + + // Property set info is invalid. + if ( m_pInfo.is() ) + m_pInfo->reset(); + + // Notify propertyset info change listeners. + if ( m_pPropSetChangeListeners && + m_pPropSetChangeListeners->getLength() ) + { + PropertySetInfoChangeEvent evt( + static_cast< OWeakObject * >( this ), + Name, + -1, + PropertySetInfoChange::PROPERTY_INSERTED ); + notifyPropertySetInfoChange( evt ); + } + + // Success. + return; + } + } + catch (const IllegalArgumentException&) + { + // insertByName + + OSL_FAIL( "PersistentPropertySet::addProperty - " + "caught IllegalArgumentException!" ); + return; + } + catch (const ElementExistException&) + { + // insertByName + + OSL_FAIL( "PersistentPropertySet::addProperty - " + "caught ElementExistException!" ); + return; + } + catch (const WrappedTargetException&) + { + // replaceByName, insertByName, commitChanges + + OSL_FAIL( "PersistentPropertySet::addProperty - " + "caught WrappedTargetException!" ); + return; + } + catch (const RuntimeException&) + { + throw; + } + catch (const Exception&) + { + // createInstance + + OSL_FAIL( "PersistentPropertySet::addProperty - " + "caught Exception!" ); + return; + } + } + + OSL_FAIL( "PersistentPropertySet::addProperty - Error!" ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::removeProperty( const OUString& Name ) +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + OUString aFullValuesName = getFullKey(); + OUString aFullPropName = aFullValuesName + "/"; + aFullPropName += makeHierarchalNameSegment( Name ); + + // Property in set? + if ( !xRootHierNameAccess->hasByHierarchicalName( aFullPropName ) ) + throw UnknownPropertyException(aFullPropName); + + // Property removable? + try + { + OUString aFullAttrName = aFullPropName + "/Attributes"; + + sal_Int32 nAttribs = 0; + if ( xRootHierNameAccess->getByHierarchicalName( aFullAttrName ) + >>= nAttribs ) + { + if ( !( nAttribs & PropertyAttribute::REMOVABLE ) ) + { + // Not removable! + throw NotRemoveableException(); + } + } + else + { + OSL_FAIL( "PersistentPropertySet::removeProperty - " + "No attributes!" ); + return; + } + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PersistentPropertySet::removeProperty - " + "caught NoSuchElementException!" ); + } + + // Remove property... + + Reference< XNameContainer > xContainer( + m_pCreator->getConfigWriteAccess( aFullValuesName ), + UNO_QUERY ); + Reference< XChangesBatch > xBatch( + m_pCreator->getConfigWriteAccess( OUString() ), + UNO_QUERY ); + + OSL_ENSURE( xBatch.is(), + "PersistentPropertySet::removeProperty - No batch!" ); + + OSL_ENSURE( xContainer.is(), + "PersistentPropertySet::removeProperty - No container!" ); + + if ( xBatch.is() && xContainer.is() ) + { + try + { + sal_Int32 nHandle = -1; + + if ( m_pPropSetChangeListeners && + m_pPropSetChangeListeners->getLength() ) + { + // Obtain property handle ( needed for propertysetinfo + // change event )... + + try + { + OUString aFullHandleName = aFullPropName + "/Handle"; + + if ( ! ( xRootHierNameAccess->getByHierarchicalName( + aFullHandleName ) >>= nHandle ) ) + nHandle = -1; + + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PersistentPropertySet::removeProperty - " + "caught NoSuchElementException!" ); + nHandle = -1; + } + } + + xContainer->removeByName( Name ); + xBatch->commitChanges(); + + // Property set info is invalid. + if ( m_pInfo.is() ) + m_pInfo->reset(); + + // Notify propertyset info change listeners. + if ( m_pPropSetChangeListeners && + m_pPropSetChangeListeners->getLength() ) + { + PropertySetInfoChangeEvent evt( + static_cast< OWeakObject * >( this ), + Name, + nHandle, + PropertySetInfoChange::PROPERTY_REMOVED ); + notifyPropertySetInfoChange( evt ); + } + + // Success. + return; + } + catch (const NoSuchElementException&) + { + // removeByName + + OSL_FAIL( "PersistentPropertySet::removeProperty - " + "caught NoSuchElementException!" ); + return; + } + catch (const WrappedTargetException&) + { + // commitChanges + + OSL_FAIL( "PersistentPropertySet::removeProperty - " + "caught WrappedTargetException!" ); + return; + } + } + } + + OSL_FAIL( "PersistentPropertySet::removeProperty - Error!" ); +} + + +// XPropertySetInfoChangeNotifier methods. + + +// virtual +void SAL_CALL PersistentPropertySet::addPropertySetInfoChangeListener( + const Reference< XPropertySetInfoChangeListener >& Listener ) +{ + if ( !m_pPropSetChangeListeners ) + m_pPropSetChangeListeners.reset( + new OInterfaceContainerHelper3<XPropertySetInfoChangeListener>( m_aMutex ) ); + + m_pPropSetChangeListeners->addInterface( Listener ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::removePropertySetInfoChangeListener( + const Reference< XPropertySetInfoChangeListener >& Listener ) +{ + if ( m_pPropSetChangeListeners ) + m_pPropSetChangeListeners->removeInterface( Listener ); +} + + +// XPropertyAccess methods. + + +// virtual +Sequence< PropertyValue > SAL_CALL PersistentPropertySet::getPropertyValues() +{ + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + try + { + Reference< XNameAccess > xNameAccess; + xRootHierNameAccess->getByHierarchicalName(getFullKey()) + >>= xNameAccess; + if ( xNameAccess.is() ) + { + // Obtain property names. + + Sequence< OUString > aElems = xNameAccess->getElementNames(); + + sal_Int32 nCount = aElems.getLength(); + if ( nCount ) + { + Reference< XHierarchicalNameAccess > xHierNameAccess( + xNameAccess, UNO_QUERY ); + + OSL_ENSURE( xHierNameAccess.is(), + "PersistentPropertySet::getPropertyValues - " + "No hierarchical name access!" ); + + if ( xHierNameAccess.is() ) + { + Sequence< PropertyValue > aValues( nCount ); + auto pValues = aValues.getArray(); + + static const OUStringLiteral aHandleName(u"/Handle"); + static const OUStringLiteral aValueName(u"/Value"); + static const OUStringLiteral aStateName(u"/State"); + + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + PropertyValue& rValue = pValues[ n ]; + OUString rName = aElems[ n ]; + OUString aXMLName + = makeHierarchalNameSegment( rName ); + + // Set property name. + + rValue.Name = rName; + + try + { + // Obtain and set property handle + OUString aHierName = aXMLName + aHandleName; + Any aKeyValue + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + if ( !( aKeyValue >>= rValue.Handle ) ) + OSL_FAIL( "PersistentPropertySet::getPropertyValues - " + "Error getting property handle!" ); + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PersistentPropertySet::getPropertyValues - " + "NoSuchElementException!" ); + } + + try + { + // Obtain and set property value + OUString aHierName = aXMLName + aValueName; + rValue.Value + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + // Note: The value may be void if addProperty + // was called with a default value + // of type void. + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PersistentPropertySet::getPropertyValues - " + "NoSuchElementException!" ); + } + + try + { + // Obtain and set property state + OUString aHierName = aXMLName +aStateName; + Any aKeyValue + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + sal_Int32 nState = 0; + if ( !( aKeyValue >>= nState ) ) + OSL_FAIL( "PersistentPropertySet::getPropertyValues - " + "Error getting property state!" ); + else + rValue.State = PropertyState( nState ); + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PersistentPropertySet::getPropertyValues - " + "NoSuchElementException!" ); + } + } + + return aValues; + } + } + } + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + } + } + + return Sequence< PropertyValue >( 0 ); +} + + +// virtual +void SAL_CALL PersistentPropertySet::setPropertyValues( + const Sequence< PropertyValue >& aProps ) +{ + if ( !aProps.hasElements() ) + return; + + osl::ClearableGuard< osl::Mutex > aCGuard( m_aMutex ); + + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pCreator->getRootConfigReadAccess(), UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + std::vector< PropertyChangeEvent > aEvents; + + OUString aFullPropNamePrefix( getFullKey() + "/" ); + + // Iterate over given property value sequence. + for ( const PropertyValue& rNewValue : aProps ) + { + const OUString& rName = rNewValue.Name; + + OUString aFullPropName = aFullPropNamePrefix; + aFullPropName += makeHierarchalNameSegment( rName ); + + // Does property exist? + if ( xRootHierNameAccess->hasByHierarchicalName( aFullPropName ) ) + { + Reference< XNameReplace > xNameReplace( + m_pCreator->getConfigWriteAccess( + aFullPropName ), UNO_QUERY ); + Reference< XChangesBatch > xBatch( + m_pCreator->getConfigWriteAccess( + OUString() ), UNO_QUERY ); + + if ( xNameReplace.is() && xBatch.is() ) + { + try + { + // Write handle + xNameReplace->replaceByName( + "Handle", + Any( rNewValue.Handle ) ); + + // Save old value + OUString aValueName = aFullPropName +"/Value"; + Any aOldValue + = xRootHierNameAccess->getByHierarchicalName( + aValueName ); + // Write value + xNameReplace->replaceByName( + "Value", + rNewValue.Value ); + + // Write state ( Now it is a directly set value ) + xNameReplace->replaceByName( + "State", + Any( + sal_Int32( + PropertyState_DIRECT_VALUE ) ) ); + + // Commit changes. + xBatch->commitChanges(); + + if ( m_pPropertyChangeListeners ) + { + PropertyChangeEvent aEvt; + aEvt.Source = static_cast<OWeakObject*>(this); + aEvt.PropertyName = rNewValue.Name; + aEvt.PropertyHandle = rNewValue.Handle; + aEvt.Further = false; + aEvt.OldValue = aOldValue; + aEvt.NewValue = rNewValue.Value; + + aEvents.push_back( aEvt ); + } + } + catch (const IllegalArgumentException&) + { + // replaceByName + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName, replaceByName + } + catch (const WrappedTargetException&) + { + // replaceByName, commitChanges + } + } + } + } + + // Callback follows! + aCGuard.clear(); + + if ( m_pPropertyChangeListeners ) + { + // Notify property changes. + for (auto const& event : aEvents) + { + notifyPropertyChangeEvent( event ); + } + } + + return; + } + + OSL_FAIL( "PersistentPropertySet::setPropertyValues - Nothing set!" ); +} + + +// Non-interface methods + + +void PersistentPropertySet::notifyPropertyChangeEvent( + const PropertyChangeEvent& rEvent ) const +{ + // Get "normal" listeners for the property. + OInterfaceContainerHelper3<XPropertyChangeListener>* pContainer = + m_pPropertyChangeListeners->getContainer( rEvent.PropertyName ); + if ( pContainer && pContainer->getLength() ) + { + pContainer->notifyEach( &XPropertyChangeListener::propertyChange, rEvent ); + } + + // Get "normal" listeners for all properties. + OInterfaceContainerHelper3<XPropertyChangeListener>* pNoNameContainer = + m_pPropertyChangeListeners->getContainer( OUString() ); + if ( pNoNameContainer && pNoNameContainer->getLength() ) + { + pNoNameContainer->notifyEach( &XPropertyChangeListener::propertyChange, rEvent ); + } +} + + +void PersistentPropertySet::notifyPropertySetInfoChange( + const PropertySetInfoChangeEvent& evt ) const +{ + if ( !m_pPropSetChangeListeners ) + return; + + // Notify event listeners. + m_pPropSetChangeListeners->notifyEach( &XPropertySetInfoChangeListener::propertySetInfoChange, evt ); +} + + +const OUString& PersistentPropertySet::getFullKey() +{ + if ( m_aFullKey.isEmpty() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + if ( m_aFullKey.isEmpty() ) + { + m_aFullKey = makeHierarchalNameSegment( m_aKey ); + m_aFullKey += "/Values"; + } + } + + return m_aFullKey; +} + + +PropertySetRegistry& PersistentPropertySet::getPropertySetRegistry() +{ + return *m_pCreator; +} + + +// PropertySetInfo_Impl Implementation. + + +PropertySetInfo_Impl::PropertySetInfo_Impl( + PersistentPropertySet* pOwner ) +: m_pOwner( pOwner ) +{ +} + + +// XPropertySetInfo methods. + + +// virtual +Sequence< Property > SAL_CALL PropertySetInfo_Impl::getProperties() +{ + if ( !m_xProps ) + { + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pOwner->getPropertySetRegistry().getRootConfigReadAccess(), + UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + try + { + Reference< XNameAccess > xNameAccess; + xRootHierNameAccess->getByHierarchicalName( + m_pOwner->getFullKey() ) + >>= xNameAccess; + if ( xNameAccess.is() ) + { + // Obtain property names. + + Sequence< OUString > aElems + = xNameAccess->getElementNames(); + + sal_uInt32 nCount = aElems.getLength(); + Sequence< Property > aPropSeq( nCount ); + + if ( nCount ) + { + Reference< XHierarchicalNameAccess > xHierNameAccess( + xNameAccess, UNO_QUERY ); + + OSL_ENSURE( xHierNameAccess.is(), + "PropertySetInfo_Impl::getProperties - " + "No hierarchical name access!" ); + + if ( xHierNameAccess.is() ) + { + static const OUStringLiteral aHandleName(u"/Handle"); + static const OUStringLiteral aValueName(u"/Value"); + static const OUStringLiteral aAttrName(u"/Attributes"); + + Property* pProps = aPropSeq.getArray(); + + for ( sal_uInt32 n = 0; n < nCount; ++n ) + { + Property& rProp = pProps[ n ]; + OUString rName = aElems[ n ]; + OUString aXMLName + = makeHierarchalNameSegment( rName ); + + // Set property name. + + rProp.Name = rName; + + try + { + // Obtain and set property handle + OUString aHierName = aXMLName + aHandleName; + Any aKeyValue + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + if ( !( aKeyValue >>= rProp.Handle ) ) + OSL_FAIL( "PropertySetInfo_Impl::getProperties - " + "Error getting property handle!" ); + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PropertySetInfo_Impl::getProperties - " + "NoSuchElementException!" ); + } + + try + { + // Obtain and set property type + OUString aHierName = aXMLName + aValueName; + Any aKeyValue + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + // Note: The type may be void if addProperty + // was called with a default value + // of type void. + + rProp.Type = aKeyValue.getValueType(); + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PropertySetInfo_Impl::getProperties - " + "NoSuchElementException!" ); + } + + try + { + // Obtain and set property attributes + OUString aHierName = aXMLName + aAttrName; + Any aKeyValue + = xHierNameAccess->getByHierarchicalName( + aHierName ); + + sal_Int32 nAttribs = 0; + if ( aKeyValue >>= nAttribs ) + rProp.Attributes + = sal_Int16( nAttribs ); + else + OSL_FAIL( "PropertySetInfo_Impl::getProperties - " + "Error getting property attributes!" ); + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PropertySetInfo_Impl::getProperties - " + "NoSuchElementException!" ); + } + } + } + } + + // Success. + m_xProps = std::move(aPropSeq); + return *m_xProps; + } + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + } + } + + OSL_FAIL( "PropertySetInfo_Impl::getProperties - Error!" ); + m_xProps.emplace(); + } + + return *m_xProps; +} + + +// virtual +Property SAL_CALL PropertySetInfo_Impl::getPropertyByName( + const OUString& aName ) +{ + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pOwner->getPropertySetRegistry().getRootConfigReadAccess(), + UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + OUString aFullPropName( m_pOwner->getFullKey() + "/" ); + aFullPropName += makeHierarchalNameSegment( aName ); + + // Does property exist? + if ( !xRootHierNameAccess->hasByHierarchicalName( aFullPropName ) ) + throw UnknownPropertyException(aFullPropName); + + try + { + Property aProp; + + // Obtain handle. + OUString aKey = aFullPropName + "/Handle"; + + if ( !( xRootHierNameAccess->getByHierarchicalName( aKey ) + >>= aProp.Handle ) ) + { + OSL_FAIL( "PropertySetInfo_Impl::getPropertyByName - " + "No handle!" ); + return Property(); + } + + // Obtain Value and extract type. + aKey = aFullPropName + "/Value"; + + Any aValue = xRootHierNameAccess->getByHierarchicalName( aKey ); + if ( !aValue.hasValue() ) + { + OSL_FAIL( "PropertySetInfo_Impl::getPropertyByName - " + "No Value!" ); + return Property(); + } + + aProp.Type = aValue.getValueType(); + + // Obtain Attributes. + aKey = aFullPropName + "/Attributes"; + + sal_Int32 nAttribs = 0; + if ( xRootHierNameAccess->getByHierarchicalName( aKey ) + >>= nAttribs ) + aProp.Attributes = sal_Int16( nAttribs ); + else + { + OSL_FAIL( "PropertySetInfo_Impl::getPropertyByName - " + "No attributes!" ); + return Property(); + } + + // set name. + aProp.Name = aName; + + // Success. + return aProp; + } + catch (const NoSuchElementException&) + { + // getByHierarchicalName + + OSL_FAIL( "PropertySetInfo_Impl::getPropertyByName - " + "caught NoSuchElementException!" ); + } + + } + + OSL_FAIL( "PropertySetInfo_Impl::getPropertyByName - Error!" ); + return Property(); +} + + +// virtual +sal_Bool SAL_CALL PropertySetInfo_Impl::hasPropertyByName( + const OUString& Name ) +{ + Reference< XHierarchicalNameAccess > xRootHierNameAccess( + m_pOwner->getPropertySetRegistry().getRootConfigReadAccess(), + UNO_QUERY ); + if ( xRootHierNameAccess.is() ) + { + OUString aFullPropName( m_pOwner->getFullKey() + "/" ); + aFullPropName += makeHierarchalNameSegment( Name ); + + return xRootHierNameAccess->hasByHierarchicalName( aFullPropName ); + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/ucb/source/core/ucbstore.hxx b/ucb/source/core/ucbstore.hxx new file mode 100644 index 000000000..8b8597f75 --- /dev/null +++ b/ucb/source/core/ucbstore.hxx @@ -0,0 +1,248 @@ +/* -*- 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 <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/ucb/XPropertySetRegistryFactory.hpp> +#include <com/sun/star/ucb/XPropertySetRegistry.hpp> +#include <com/sun/star/ucb/XPersistentPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> +#include <com/sun/star/beans/XPropertySetInfoChangeNotifier.hpp> +#include <com/sun/star/beans/XPropertyAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <comphelper/interfacecontainer3.hxx> +#include <comphelper/multiinterfacecontainer3.hxx> +#include <comphelper/compbase.hxx> +#include <rtl/ref.hxx> +#include <unordered_map> + + +using UcbStore_Base = comphelper::WeakComponentImplHelper < + css::lang::XServiceInfo, + css::ucb::XPropertySetRegistryFactory, + css::lang::XInitialization >; + +class UcbStore : public UcbStore_Base +{ + css::uno::Reference< css::uno::XComponentContext > m_xContext; + css::uno::Sequence< css::uno::Any > m_aInitArgs; + css::uno::Reference< css::ucb::XPropertySetRegistry > m_xTheRegistry; + +public: + explicit UcbStore( const css::uno::Reference< css::uno::XComponentContext >& xContext ); + virtual ~UcbStore() override; + + // 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; + + // XPropertySetRegistryFactory + virtual css::uno::Reference< css::ucb::XPropertySetRegistry > SAL_CALL + createPropertySetRegistry( const OUString& URL ) override; + + // XInitialization + virtual void SAL_CALL + initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; +}; + + +class PersistentPropertySet; + +// PropertySetMap_Impl. +typedef std::unordered_map< OUString, PersistentPropertySet*> PropertySetMap_Impl; + +class PropertySetRegistry : public cppu::WeakImplHelper < + css::lang::XServiceInfo, + css::ucb::XPropertySetRegistry, + css::container::XNameAccess > +{ + friend class PersistentPropertySet; + + css::uno::Reference< css::uno::XComponentContext > m_xContext; + const css::uno::Sequence< css::uno::Any > m_aInitArgs; + PropertySetMap_Impl m_aPropSets; + css::uno::Reference< css::lang::XMultiServiceFactory > m_xConfigProvider; + css::uno::Reference< css::uno::XInterface > m_xRootReadAccess; + css::uno::Reference< css::uno::XInterface > m_xRootWriteAccess; + osl::Mutex m_aMutex; + bool m_bTriedToGetRootReadAccess; + bool m_bTriedToGetRootWriteAccess; + +private: + css::uno::Reference< css::lang::XMultiServiceFactory > + getConfigProvider(); + + void add ( PersistentPropertySet* pSet ); + void remove( PersistentPropertySet* pSet ); + + void renamePropertySet( const OUString& rOldKey, + const OUString& rNewKey ); + +public: + PropertySetRegistry( + const css::uno::Reference< css::uno::XComponentContext >& xContext, + const css::uno::Sequence< css::uno::Any >& rInitArgs); + virtual ~PropertySetRegistry() override; + + + // 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; + + // XPropertySetRegistry + virtual css::uno::Reference< css::ucb::XPersistentPropertySet > SAL_CALL + openPropertySet( const OUString& key, sal_Bool create ) override; + virtual void SAL_CALL + removePropertySet( const OUString& key ) override; + + // XElementAccess ( XNameAccess is derived from it ) + virtual css::uno::Type SAL_CALL + getElementType() override; + virtual sal_Bool SAL_CALL + hasElements() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL + getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL + getElementNames() override; + virtual sal_Bool SAL_CALL + hasByName( const OUString& aName ) override; + + // Non-interface methods + css::uno::Reference< css::uno::XInterface > + getRootConfigReadAccess(); + css::uno::Reference< css::uno::XInterface > + getConfigWriteAccess( const OUString& rPath ); +}; + + +class PropertySetInfo_Impl; +typedef comphelper::OMultiTypeInterfaceContainerHelperVar3<css::beans::XPropertyChangeListener, OUString> PropertyListeners_Impl; + +class PersistentPropertySet : public cppu::WeakImplHelper < + css::lang::XServiceInfo, + css::lang::XComponent, + css::ucb::XPersistentPropertySet, + css::container::XNamed, + css::beans::XPropertyContainer, + css::beans::XPropertySetInfoChangeNotifier, + css::beans::XPropertyAccess > +{ + rtl::Reference<PropertySetRegistry> m_pCreator; + rtl::Reference<PropertySetInfo_Impl> m_pInfo; + OUString m_aKey; + OUString m_aFullKey; + osl::Mutex m_aMutex; + std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::lang::XEventListener>> m_pDisposeEventListeners; + std::unique_ptr<comphelper::OInterfaceContainerHelper3<css::beans::XPropertySetInfoChangeListener>> m_pPropSetChangeListeners; + std::unique_ptr<PropertyListeners_Impl> m_pPropertyChangeListeners; + +private: + void notifyPropertyChangeEvent( + const css::beans::PropertyChangeEvent& rEvent ) const; + void notifyPropertySetInfoChange( + const css::beans::PropertySetInfoChangeEvent& evt ) const; + +public: + PersistentPropertySet( + PropertySetRegistry& rCreator, + const OUString& rKey ); + virtual ~PersistentPropertySet() override; + + // 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; + + // XComponent + virtual void SAL_CALL + dispose() override; + virtual void SAL_CALL + addEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) override; + virtual void SAL_CALL + removeEventListener( const css::uno::Reference< css::lang::XEventListener >& Listener ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL + getPropertySetInfo() override; + virtual void SAL_CALL + setPropertyValue( const OUString& aPropertyName, + const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL + getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL + addPropertyChangeListener( const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL + removePropertyChangeListener( const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL + addVetoableChangeListener( const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL + removeVetoableChangeListener( const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XPersistentPropertySet + virtual css::uno::Reference< css::ucb::XPropertySetRegistry > SAL_CALL + getRegistry() override; + virtual OUString SAL_CALL + getKey() override; + + // XNamed + virtual OUString SAL_CALL + getName() override; + virtual void SAL_CALL + setName( const OUString& aName ) override; + + // XPropertyContainer + virtual void SAL_CALL + addProperty( const OUString& Name, + sal_Int16 Attributes, + const css::uno::Any& DefaultValue ) override; + virtual void SAL_CALL + removeProperty( const OUString& Name ) override; + + // XPropertySetInfoChangeNotifier + virtual void SAL_CALL + addPropertySetInfoChangeListener( const css::uno::Reference< css::beans::XPropertySetInfoChangeListener >& Listener ) override; + virtual void SAL_CALL + removePropertySetInfoChangeListener( const css::uno::Reference< css::beans::XPropertySetInfoChangeListener >& Listener ) override; + + // XPropertyAccess + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL + getPropertyValues() override; + virtual void SAL_CALL + setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue >& aProps ) override; + + // Non-interface methods. + PropertySetRegistry& getPropertySetRegistry(); + const OUString& getFullKey(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |