summaryrefslogtreecommitdiffstats
path: root/ucb/source/ucp/tdoc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ucb/source/ucp/tdoc/tdoc_content.cxx2827
-rw-r--r--ucb/source/ucp/tdoc/tdoc_content.hxx282
-rw-r--r--ucb/source/ucp/tdoc/tdoc_contentcaps.cxx622
-rw-r--r--ucb/source/ucp/tdoc/tdoc_datasupplier.cxx380
-rw-r--r--ucb/source/ucp/tdoc/tdoc_datasupplier.hxx86
-rw-r--r--ucb/source/ucp/tdoc/tdoc_docmgr.cxx657
-rw-r--r--ucb/source/ucp/tdoc/tdoc_docmgr.hxx151
-rw-r--r--ucb/source/ucp/tdoc/tdoc_documentcontentfactory.cxx114
-rw-r--r--ucb/source/ucp/tdoc/tdoc_documentcontentfactory.hxx58
-rw-r--r--ucb/source/ucp/tdoc/tdoc_passwordrequest.cxx191
-rw-r--r--ucb/source/ucp/tdoc/tdoc_passwordrequest.hxx87
-rw-r--r--ucb/source/ucp/tdoc/tdoc_provider.cxx605
-rw-r--r--ucb/source/ucp/tdoc/tdoc_provider.hxx144
-rw-r--r--ucb/source/ucp/tdoc/tdoc_resultset.cxx78
-rw-r--r--ucb/source/ucp/tdoc/tdoc_resultset.hxx47
-rw-r--r--ucb/source/ucp/tdoc/tdoc_stgelems.cxx876
-rw-r--r--ucb/source/ucp/tdoc/tdoc_stgelems.hxx336
-rw-r--r--ucb/source/ucp/tdoc/tdoc_storage.cxx617
-rw-r--r--ucb/source/ucp/tdoc/tdoc_storage.hxx162
-rw-r--r--ucb/source/ucp/tdoc/tdoc_uri.cxx111
-rw-r--r--ucb/source/ucp/tdoc/tdoc_uri.hxx107
-rw-r--r--ucb/source/ucp/tdoc/ucptdoc1.component30
22 files changed, 8568 insertions, 0 deletions
diff --git a/ucb/source/ucp/tdoc/tdoc_content.cxx b/ucb/source/ucp/tdoc/tdoc_content.cxx
new file mode 100644
index 000000000..9323380bb
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_content.cxx
@@ -0,0 +1,2827 @@
+/* -*- 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 <o3tl/string_view.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/beans/IllegalTypeException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/lang/IllegalAccessException.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/task/DocumentPasswordRequest.hpp>
+#include <com/sun/star/task/XInteractionPassword.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentAction.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
+#include <com/sun/star/ucb/MissingInputStreamException.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/TransferInfo.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
+#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
+#include <com/sun/star/ucb/XCommandInfo.hpp>
+#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <ucbhelper/cancelcommandexecution.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/propertyvalueset.hxx>
+#include <ucbhelper/macros.hxx>
+
+#include "tdoc_content.hxx"
+#include "tdoc_resultset.hxx"
+#include "tdoc_passwordrequest.hxx"
+
+#include "../inc/urihelper.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+static ContentType lcl_getContentType( std::u16string_view rType )
+{
+ if ( rType == TDOC_ROOT_CONTENT_TYPE )
+ return ROOT;
+ else if ( rType == TDOC_DOCUMENT_CONTENT_TYPE )
+ return DOCUMENT;
+ else if ( rType == TDOC_FOLDER_CONTENT_TYPE )
+ return FOLDER;
+ else if ( rType == TDOC_STREAM_CONTENT_TYPE )
+ return STREAM;
+ else
+ {
+ OSL_FAIL( "Content::Content - unsupported content type string" );
+ return STREAM;
+ }
+}
+
+
+// Content Implementation.
+
+
+// static ( "virtual" ctor )
+rtl::Reference<Content> Content::create(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ // Fail, if resource does not exist.
+ ContentProperties aProps;
+ if ( !Content::loadData( pProvider,
+ Uri( Identifier->getContentIdentifier() ),
+ aProps ) )
+ return nullptr;
+
+ return new Content( rxContext, pProvider, Identifier, aProps );
+}
+
+
+// static ( "virtual" ctor )
+rtl::Reference<Content> Content::create(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ const ucb::ContentInfo& Info )
+{
+ if ( Info.Type.isEmpty() )
+ return nullptr;
+
+ if ( Info.Type != TDOC_FOLDER_CONTENT_TYPE && Info.Type != TDOC_STREAM_CONTENT_TYPE )
+ {
+ OSL_FAIL( "Content::create - unsupported content type!" );
+ return nullptr;
+ }
+
+ return new Content( rxContext, pProvider, Identifier, Info );
+}
+
+
+Content::Content(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ ContentProvider * pProvider,
+ const uno::Reference< ucb::XContentIdentifier > & Identifier,
+ const ContentProperties & rProps )
+: ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_aProps( rProps ),
+ m_eState( PERSISTENT ),
+ m_pProvider( pProvider )
+{
+}
+
+
+// ctor for a content just created via XContentCreator::createNewContent()
+Content::Content(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const uno::Reference< ucb::XContentIdentifier >& Identifier,
+ const ucb::ContentInfo& Info )
+ : ContentImplHelper( rxContext, pProvider, Identifier ),
+ m_aProps( lcl_getContentType( Info.Type ), OUString() ), // no Title (yet)
+ m_eState( TRANSIENT ),
+ m_pProvider( pProvider )
+{
+}
+
+
+// virtual
+Content::~Content()
+{
+}
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL Content::acquire()
+ noexcept
+{
+ ContentImplHelper::acquire();
+}
+
+
+// virtual
+void SAL_CALL Content::release()
+ noexcept
+{
+ ContentImplHelper::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
+{
+ uno::Any aRet = ContentImplHelper::queryInterface( rType );
+
+ if ( !aRet.hasValue() )
+ {
+ aRet = cppu::queryInterface(
+ rType, static_cast< ucb::XContentCreator * >( this ) );
+ if ( aRet.hasValue() )
+ {
+ if ( !m_aProps.isContentCreator() )
+ return uno::Any();
+ }
+ }
+
+ return aRet;
+}
+
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_COMMON_IMPL( Content );
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
+{
+ if ( m_aProps.isContentCreator() )
+ {
+ static cppu::OTypeCollection s_aFolderTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ),
+ CPPU_TYPE_REF( ucb::XContentCreator ) );
+
+ return s_aFolderTypes.getTypes();
+ }
+ else
+ {
+ static cppu::OTypeCollection s_aDocumentTypes(
+ CPPU_TYPE_REF( lang::XTypeProvider ),
+ CPPU_TYPE_REF( lang::XServiceInfo ),
+ CPPU_TYPE_REF( lang::XComponent ),
+ CPPU_TYPE_REF( ucb::XContent ),
+ CPPU_TYPE_REF( ucb::XCommandProcessor ),
+ CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
+ CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
+ CPPU_TYPE_REF( beans::XPropertyContainer ),
+ CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
+ CPPU_TYPE_REF( container::XChild ) );
+
+ return s_aDocumentTypes.getTypes();
+ }
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL Content::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.TransientDocumentsContent";
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< OUString > aSNS( 1 );
+
+ if ( m_aProps.getType() == STREAM )
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsStreamContent";
+ else if ( m_aProps.getType() == FOLDER )
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsFolderContent";
+ else if ( m_aProps.getType() == DOCUMENT )
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsDocumentContent";
+ else
+ aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsRootContent";
+
+ return aSNS;
+}
+
+
+// XContent methods.
+
+
+// virtual
+OUString SAL_CALL Content::getContentType()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ return m_aProps.getContentType();
+}
+
+
+// virtual
+uno::Reference< ucb::XContentIdentifier > SAL_CALL
+Content::getIdentifier()
+{
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Transient?
+ if ( m_eState == TRANSIENT )
+ {
+ // Transient contents have no identifier.
+ return uno::Reference< ucb::XContentIdentifier >();
+ }
+ }
+ return ContentImplHelper::getIdentifier();
+}
+
+
+// XCommandProcessor methods.
+
+
+// virtual
+uno::Any SAL_CALL Content::execute(
+ const ucb::Command& aCommand,
+ sal_Int32 /*CommandId*/,
+ const uno::Reference< ucb::XCommandEnvironment >& Environment )
+{
+ uno::Any aRet;
+
+ if ( aCommand.Name == "getPropertyValues" )
+ {
+
+ // getPropertyValues
+
+
+ uno::Sequence< beans::Property > Properties;
+ if ( !( aCommand.Argument >>= Properties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= getPropertyValues( Properties );
+ }
+ else if ( aCommand.Name == "setPropertyValues" )
+ {
+
+ // setPropertyValues
+
+
+ uno::Sequence< beans::PropertyValue > aProperties;
+ if ( !( aCommand.Argument >>= aProperties ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( !aProperties.hasElements() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "No properties!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= setPropertyValues( aProperties, Environment );
+ }
+ else if ( aCommand.Name == "getPropertySetInfo" )
+ {
+
+ // getPropertySetInfo
+
+
+ aRet <<= getPropertySetInfo( Environment );
+ }
+ else if ( aCommand.Name == "getCommandInfo" )
+ {
+
+ // getCommandInfo
+
+
+ aRet <<= getCommandInfo( Environment );
+ }
+ else if ( aCommand.Name == "open" )
+ {
+
+ // open
+
+
+ ucb::OpenCommandArgument2 aOpenCommand;
+ if ( !( aCommand.Argument >>= aOpenCommand ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet = open( aOpenCommand, Environment );
+ }
+ else if ( aCommand.Name == "insert" )
+ {
+
+ // insert ( Supported by folders and streams only )
+
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType != FOLDER ) && ( eType != STREAM ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "insert command only supported by "
+ "folders and streams!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ if ( eType == STREAM )
+ {
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+ Uri aParentUri( aUri.getParentUri() );
+ if ( aParentUri.isDocument() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "insert command not supported by "
+ "streams that are direct children "
+ "of document root!",
+ static_cast< cppu::OWeakObject * >(
+ this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ ucb::InsertCommandArgument aArg;
+ if ( !( aCommand.Argument >>= aArg ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ sal_Int32 nNameClash = aArg.ReplaceExisting
+ ? ucb::NameClash::OVERWRITE
+ : ucb::NameClash::ERROR;
+ insert( aArg.Data, nNameClash, Environment );
+ }
+ else if ( aCommand.Name == "delete" )
+ {
+
+ // delete ( Supported by folders and streams only )
+
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType != FOLDER ) && ( eType != STREAM ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "delete command only supported by "
+ "folders and streams!",
+ static_cast< cppu::OWeakObject * >(
+ this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ bool bDeletePhysical = false;
+ aCommand.Argument >>= bDeletePhysical;
+ destroy( bDeletePhysical, Environment );
+
+ // Remove own and all children's persistent data.
+ if ( !removeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ Environment,
+ "Cannot remove persistent data!",
+ this );
+ // Unreachable
+ }
+
+ // Remove own and all children's Additional Core Properties.
+ removeAdditionalPropertySet();
+ }
+ else if ( aCommand.Name == "transfer" )
+ {
+
+ // transfer ( Supported by document and folders only )
+
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "transfer command only supported "
+ "by folders and documents!",
+ static_cast< cppu::OWeakObject * >(
+ this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ ucb::TransferInfo aInfo;
+ if ( !( aCommand.Argument >>= aInfo ) )
+ {
+ OSL_FAIL( "Wrong argument type!" );
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ transfer( aInfo, Environment );
+ }
+ else if ( aCommand.Name == "createNewContent" )
+ {
+
+ // createNewContent ( Supported by document and folders only )
+
+
+ {
+ osl::MutexGuard aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "createNewContent command only "
+ "supported by folders and "
+ "documents!",
+ static_cast< cppu::OWeakObject * >(
+ this ) ) ),
+ Environment );
+ // Unreachable
+ }
+ }
+
+ ucb::ContentInfo aInfo;
+ if ( !( aCommand.Argument >>= aInfo ) )
+ {
+ OSL_FAIL( "Wrong argument type!" );
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Wrong argument type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ Environment );
+ // Unreachable
+ }
+
+ aRet <<= createNewContent( aInfo );
+ }
+ else
+ {
+
+ // Unsupported command
+
+
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ Environment );
+ // Unreachable
+ }
+
+ return aRet;
+}
+
+
+// virtual
+void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
+{
+}
+
+
+// XContentCreator methods.
+
+
+// virtual
+uno::Sequence< ucb::ContentInfo > SAL_CALL
+Content::queryCreatableContentsInfo()
+{
+ return m_aProps.getCreatableContentsInfo();
+}
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+Content::createNewContent( const ucb::ContentInfo& Info )
+{
+ if ( m_aProps.isContentCreator() )
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( Info.Type.isEmpty() )
+ return uno::Reference< ucb::XContent >();
+
+ bool bCreateFolder = Info.Type == TDOC_FOLDER_CONTENT_TYPE;
+
+ // streams cannot be created as direct children of document root
+ if ( !bCreateFolder && ( m_aProps.getType() == DOCUMENT ) )
+ {
+ OSL_FAIL( "Content::createNewContent - streams cannot be "
+ "created as direct children of document root!" );
+ return uno::Reference< ucb::XContent >();
+ }
+ if ( !bCreateFolder && Info.Type != TDOC_STREAM_CONTENT_TYPE )
+ {
+ OSL_FAIL( "Content::createNewContent - unsupported type!" );
+ return uno::Reference< ucb::XContent >();
+ }
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+
+ OSL_ENSURE( !aURL.isEmpty(),
+ "Content::createNewContent - empty identifier!" );
+
+ if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
+ aURL += "/";
+
+ if ( bCreateFolder )
+ aURL += "New_Folder";
+ else
+ aURL += "New_Stream";
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aURL );
+
+ return create( m_xContext, m_pProvider, xId, Info );
+ }
+ else
+ {
+ OSL_FAIL( "createNewContent called on non-contentcreator object!" );
+ return uno::Reference< ucb::XContent >();
+ }
+}
+
+
+// virtual
+OUString Content::getParentURL()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+ return aUri.getParentUri();
+}
+
+
+uno::Reference< ucb::XContentIdentifier >
+Content::makeNewIdentifier( const OUString& rTitle )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Assemble new content identifier...
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+ OUString aNewURL = aUri.getParentUri() + ::ucb_impl::urihelper::encodeSegment( rTitle );
+
+ return
+ uno::Reference< ucb::XContentIdentifier >(
+ new ::ucbhelper::ContentIdentifier( aNewURL ) );
+}
+
+
+void Content::queryChildren( ContentRefList& rChildren )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ // Only folders (root, documents, folders) have children.
+ if ( !m_aProps.getIsFolder() )
+ return;
+
+ // Obtain a list with a snapshot of all currently instantiated contents
+ // from provider and extract the contents which are direct children
+ // of this content.
+
+ ::ucbhelper::ContentRefList aAllContents;
+ m_xProvider->queryExistingContents( aAllContents );
+
+ OUString aURL = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
+
+ if ( nURLPos != ( aURL.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aURL += "/";
+ }
+
+ sal_Int32 nLen = aURL.getLength();
+
+ for ( const auto& rContent : aAllContents )
+ {
+ ::ucbhelper::ContentImplHelperRef xChild = rContent;
+ OUString aChildURL
+ = xChild->getIdentifier()->getContentIdentifier();
+
+ // Is aURL a prefix of aChildURL?
+ if ( ( aChildURL.getLength() > nLen ) &&
+ ( aChildURL.startsWith( aURL ) ) )
+ {
+ sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
+
+ if ( ( nPos == -1 ) ||
+ ( nPos == ( aChildURL.getLength() - 1 ) ) )
+ {
+ // No further slashes / only a final slash. It's a child!
+ rChildren.emplace_back(
+ static_cast< Content * >( xChild.get() ) );
+ }
+ }
+ }
+}
+
+
+bool Content::exchangeIdentity(
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ if ( !xNewId.is() )
+ return false;
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Already persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ OSL_FAIL( "Content::exchangeIdentity - Not persistent!" );
+ return false;
+ }
+
+ // Only folders and streams can be renamed -> exchange identity.
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
+ {
+ OSL_FAIL( "Content::exchangeIdentity - "
+ "Not supported by root or document!" );
+ return false;
+ }
+
+ // Exchange own identity.
+
+ // Fail, if a content with given id already exists.
+ if ( !hasData( Uri( xNewId->getContentIdentifier() ) ) )
+ {
+ OUString aOldURL = m_xIdentifier->getContentIdentifier();
+
+ aGuard.clear();
+ if ( exchange( xNewId ) )
+ {
+ if ( eType == FOLDER )
+ {
+ // Process instantiated children...
+
+ ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( const auto& rChild : aChildren )
+ {
+ ContentRef xChild = rChild;
+
+ // Create new content identifier for the child...
+ uno::Reference< ucb::XContentIdentifier > xOldChildId
+ = xChild->getIdentifier();
+ OUString aOldChildURL
+ = xOldChildId->getContentIdentifier();
+ OUString aNewChildURL
+ = aOldChildURL.replaceAt(
+ 0,
+ aOldURL.getLength(),
+ xNewId->getContentIdentifier() );
+ uno::Reference< ucb::XContentIdentifier > xNewChildId
+ = new ::ucbhelper::ContentIdentifier( aNewChildURL );
+
+ if ( !xChild->exchangeIdentity( xNewChildId ) )
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ OSL_FAIL( "Content::exchangeIdentity - "
+ "Panic! Cannot exchange identity!" );
+ return false;
+}
+
+
+// static
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ ContentProvider* pProvider,
+ const OUString& rContentId )
+{
+ ContentProperties aData;
+ if ( loadData( pProvider, Uri(rContentId), aData ) )
+ {
+ return getPropertyValues(
+ rxContext, rProperties, aData, pProvider, rContentId );
+ }
+ else
+ {
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ for ( const beans::Property& rProp : rProperties )
+ xRow->appendVoid( rProp );
+
+ return xRow;
+ }
+}
+
+
+// static
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Sequence< beans::Property >& rProperties,
+ const ContentProperties& rData,
+ ContentProvider* pProvider,
+ const OUString& rContentId )
+{
+ // Note: Empty sequence means "get values of all supported properties".
+
+ rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
+ = new ::ucbhelper::PropertyValueSet( rxContext );
+
+ if ( rProperties.hasElements() )
+ {
+ uno::Reference< beans::XPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ for ( const beans::Property& rProp : rProperties )
+ {
+ // Process Core properties.
+
+ if ( rProp.Name == "ContentType" )
+ {
+ xRow->appendString ( rProp, rData.getContentType() );
+ }
+ else if ( rProp.Name == "Title" )
+ {
+ xRow->appendString ( rProp, rData.getTitle() );
+ }
+ else if ( rProp.Name == "IsDocument" )
+ {
+ xRow->appendBoolean( rProp, rData.getIsDocument() );
+ }
+ else if ( rProp.Name == "IsFolder" )
+ {
+ xRow->appendBoolean( rProp, rData.getIsFolder() );
+ }
+ else if ( rProp.Name == "CreatableContentsInfo" )
+ {
+ xRow->appendObject(
+ rProp, uno::Any( rData.getCreatableContentsInfo() ) );
+ }
+ else if ( rProp.Name == "Storage" )
+ {
+ // Storage is only supported by folders.
+ ContentType eType = rData.getType();
+ if ( eType == FOLDER )
+ xRow->appendObject(
+ rProp,
+ uno::Any(
+ pProvider->queryStorageClone( rContentId ) ) );
+ else
+ xRow->appendVoid( rProp );
+ }
+ else if ( rProp.Name == "DocumentModel" )
+ {
+ // DocumentModel is only supported by documents.
+ ContentType eType = rData.getType();
+ if ( eType == DOCUMENT )
+ xRow->appendObject(
+ rProp,
+ uno::Any(
+ pProvider->queryDocumentModel( rContentId ) ) );
+ else
+ xRow->appendVoid( rProp );
+ }
+ else
+ {
+ // Not a Core Property! Maybe it's an Additional Core Property?!
+
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet =
+ pProvider->getAdditionalPropertySet( rContentId,
+ false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ if ( !xRow->appendPropertySetValue(
+ xAdditionalPropSet,
+ rProp ) )
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ else
+ {
+ // Append empty entry.
+ xRow->appendVoid( rProp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // Append all Core Properties.
+ xRow->appendString (
+ beans::Property( "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getContentType() );
+
+ ContentType eType = rData.getType();
+
+ xRow->appendString (
+ beans::Property( "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ // Title is read-only for root and documents.
+ beans::PropertyAttribute::BOUND |
+ ( ( eType == ROOT ) || ( eType == DOCUMENT )
+ ? beans::PropertyAttribute::READONLY
+ : 0 ) ),
+ rData.getTitle() );
+ xRow->appendBoolean(
+ beans::Property( "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getIsDocument() );
+ xRow->appendBoolean(
+ beans::Property( "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ rData.getIsFolder() );
+ xRow->appendObject(
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ uno::Any( rData.getCreatableContentsInfo() ) );
+
+ // Storage is only supported by folders.
+ if ( eType == FOLDER )
+ xRow->appendObject(
+ beans::Property( "Storage",
+ -1,
+ cppu::UnoType<embed::XStorage>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ uno::Any( pProvider->queryStorageClone( rContentId ) ) );
+
+ // DocumentModel is only supported by documents.
+ if ( eType == DOCUMENT )
+ xRow->appendObject(
+ beans::Property( "DocumentModel",
+ -1,
+ cppu::UnoType<frame::XModel>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY ),
+ uno::Any(
+ pProvider->queryDocumentModel( rContentId ) ) );
+
+ // Append all Additional Core Properties.
+
+ uno::Reference< beans::XPropertySet > xSet =
+ pProvider->getAdditionalPropertySet( rContentId, false );
+ xRow->appendPropertySet( xSet );
+ }
+
+ return xRow;
+}
+
+
+uno::Reference< sdbc::XRow > Content::getPropertyValues(
+ const uno::Sequence< beans::Property >& rProperties )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ return getPropertyValues( m_xContext,
+ rProperties,
+ m_aProps,
+ m_pProvider,
+ m_xIdentifier->getContentIdentifier() );
+}
+
+
+uno::Sequence< uno::Any > Content::setPropertyValues(
+ const uno::Sequence< beans::PropertyValue >& rValues,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Sequence< uno::Any > aRet( rValues.getLength() );
+ auto aRetRange = asNonConstRange(aRet);
+ uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
+ sal_Int32 nChanged = 0;
+
+ beans::PropertyChangeEvent aEvent;
+ aEvent.Source = static_cast< cppu::OWeakObject * >( this );
+ aEvent.Further = false;
+ // aEvent.PropertyName =
+ aEvent.PropertyHandle = -1;
+ // aEvent.OldValue =
+ // aEvent.NewValue =
+
+ const beans::PropertyValue* pValues = rValues.getConstArray();
+ sal_Int32 nCount = rValues.getLength();
+
+ uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
+ bool bTriedToGetAdditionalPropSet = false;
+
+ bool bExchange = false;
+ OUString aOldTitle;
+ sal_Int32 nTitlePos = -1;
+
+ for ( sal_Int32 n = 0; n < nCount; ++n )
+ {
+ const beans::PropertyValue& rValue = pValues[ n ];
+
+ if ( rValue.Name == "ContentType" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "IsDocument" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "IsFolder" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "CreatableContentsInfo" )
+ {
+ // Read-only property!
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else if ( rValue.Name == "Title" )
+ {
+ // Title is read-only for root and documents.
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
+ {
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ OUString aNewValue;
+ if ( rValue.Value >>= aNewValue )
+ {
+ // No empty titles!
+ if ( !aNewValue.isEmpty() )
+ {
+ if ( aNewValue != m_aProps.getTitle() )
+ {
+ // modified title -> modified URL -> exchange !
+ if ( m_eState == PERSISTENT )
+ bExchange = true;
+
+ aOldTitle = m_aProps.getTitle();
+ m_aProps.setTitle( aNewValue );
+
+ // property change event will be sent later...
+
+ // remember position within sequence of values
+ // (for error handling).
+ nTitlePos = n;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= lang::IllegalArgumentException(
+ "Empty Title not allowed!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 );
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= beans::IllegalTypeException(
+ "Title Property value has wrong type!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+ else if ( rValue.Name == "Storage" )
+ {
+ ContentType eType = m_aProps.getType();
+ if ( eType == FOLDER )
+ {
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ // Storage is only supported by folders.
+ aRetRange[ n ] <<= beans::UnknownPropertyException(
+ "Storage property only supported by folders",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else if ( rValue.Name == "DocumentModel" )
+ {
+ ContentType eType = m_aProps.getType();
+ if ( eType == DOCUMENT )
+ {
+ aRetRange[ n ] <<= lang::IllegalAccessException(
+ "Property is read-only!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ else
+ {
+ // Storage is only supported by folders.
+ aRetRange[ n ] <<= beans::UnknownPropertyException(
+ "DocumentModel property only supported by documents",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ else
+ {
+ // Not a Core Property! Maybe it's an Additional Core Property?!
+
+ if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
+ {
+ xAdditionalPropSet = getAdditionalPropertySet( false );
+ bTriedToGetAdditionalPropSet = true;
+ }
+
+ if ( xAdditionalPropSet.is() )
+ {
+ try
+ {
+ uno::Any aOldValue = xAdditionalPropSet->getPropertyValue(
+ rValue.Name );
+ if ( aOldValue != rValue.Value )
+ {
+ xAdditionalPropSet->setPropertyValue(
+ rValue.Name, rValue.Value );
+
+ aEvent.PropertyName = rValue.Name;
+ aEvent.OldValue = aOldValue;
+ aEvent.NewValue = rValue.Value;
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+ }
+ catch ( beans::UnknownPropertyException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::WrappedTargetException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( beans::PropertyVetoException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ catch ( lang::IllegalArgumentException const & e )
+ {
+ aRetRange[ n ] <<= e;
+ }
+ }
+ else
+ {
+ aRetRange[ n ] <<= uno::Exception(
+ "No property set for storing the value!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+ }
+
+ if ( bExchange )
+ {
+ uno::Reference< ucb::XContentIdentifier > xOldId
+ = m_xIdentifier;
+ uno::Reference< ucb::XContentIdentifier > xNewId
+ = makeNewIdentifier( m_aProps.getTitle() );
+
+ aGuard.clear();
+ if ( exchangeIdentity( xNewId ) )
+ {
+ // Adapt persistent data.
+ renameData( xOldId, xNewId );
+
+ // Adapt Additional Core Properties.
+ renameAdditionalPropertySet( xOldId->getContentIdentifier(),
+ xNewId->getContentIdentifier() );
+ }
+ else
+ {
+ // Roll-back.
+ m_aProps.setTitle( aOldTitle );
+ aOldTitle.clear();
+
+ // Set error .
+ aRetRange[ nTitlePos ] <<= uno::Exception(
+ "Exchange failed!",
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ }
+
+ if ( !aOldTitle.isEmpty() )
+ {
+ aEvent.PropertyName = "Title";
+ aEvent.OldValue <<= aOldTitle;
+ aEvent.NewValue <<= m_aProps.getTitle();
+
+ aChanges.getArray()[ nChanged ] = aEvent;
+ nChanged++;
+ }
+
+ if ( nChanged > 0 )
+ {
+ // Save changes, if content was already made persistent.
+ if ( !bExchange && ( m_eState == PERSISTENT ) )
+ {
+ if ( !storeData( uno::Reference< io::XInputStream >(), xEnv ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot store persistent data!",
+ this );
+ // Unreachable
+ }
+ }
+
+ aChanges.realloc( nChanged );
+
+ aGuard.clear();
+ notifyPropertiesChange( aChanges );
+ }
+
+ return aRet;
+}
+
+
+uno::Any Content::open(
+ const ucb::OpenCommandArgument2& rArg,
+ const uno::Reference< ucb::XCommandEnvironment >& xEnv )
+{
+ if ( rArg.Mode == ucb::OpenMode::ALL ||
+ rArg.Mode == ucb::OpenMode::FOLDERS ||
+ rArg.Mode == ucb::OpenMode::DOCUMENTS )
+ {
+
+ // open command for a folder content
+
+
+ uno::Reference< ucb::XDynamicResultSet > xSet
+ = new DynamicResultSet( m_xContext, this, rArg );
+ return uno::Any( xSet );
+ }
+ else
+ {
+
+ // open command for a document content
+
+
+ if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
+ ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
+ {
+ // Currently(?) unsupported.
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedOpenModeException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ sal_Int16( rArg.Mode ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ uno::Reference< io::XActiveDataStreamer > xDataStreamer(
+ rArg.Sink, uno::UNO_QUERY );
+ if ( xDataStreamer.is() )
+ {
+ // May throw CommandFailedException, DocumentPasswordRequest!
+ uno::Reference< io::XStream > xStream = getStream( xEnv );
+ if ( !xStream.is() )
+ {
+ // No interaction if we are not persistent!
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ m_eState == PERSISTENT
+ ? xEnv
+ : uno::Reference< ucb::XCommandEnvironment >(),
+ "Got no data stream!",
+ this );
+ // Unreachable
+ }
+
+ // Done.
+ xDataStreamer->setStream( xStream );
+ }
+ else
+ {
+ uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
+ if ( xOut.is() )
+ {
+ // PUSH: write data into xOut
+
+ // May throw CommandFailedException, DocumentPasswordRequest!
+ uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
+ if ( !xIn.is() )
+ {
+ // No interaction if we are not persistent!
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ m_eState == PERSISTENT
+ ? xEnv
+ : uno::Reference< ucb::XCommandEnvironment >(),
+ "Got no data stream!",
+ this );
+ // Unreachable
+ }
+
+ try
+ {
+ uno::Sequence< sal_Int8 > aBuffer;
+
+ while (true)
+ {
+ sal_Int32 nRead = xIn->readSomeBytes( aBuffer, 65536 );
+ if (!nRead)
+ break;
+ aBuffer.realloc( nRead );
+ xOut->writeBytes( aBuffer );
+ }
+
+ xOut->closeOutput();
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // closeOutput, readSomeBytes, writeBytes
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // closeOutput, readSomeBytes, writeBytes
+ }
+ catch ( io::IOException const & )
+ {
+ // closeOutput, readSomeBytes, writeBytes
+ }
+ }
+ else
+ {
+ uno::Reference< io::XActiveDataSink > xDataSink(
+ rArg.Sink, uno::UNO_QUERY );
+ if ( xDataSink.is() )
+ {
+ // PULL: wait for client read
+
+ // May throw CommandFailedException, DocumentPasswordRequest!
+ uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
+ if ( !xIn.is() )
+ {
+ // No interaction if we are not persistent!
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ m_eState == PERSISTENT
+ ? xEnv
+ : uno::Reference<
+ ucb::XCommandEnvironment >(),
+ "Got no data stream!",
+ this );
+ // Unreachable
+ }
+
+ // Done.
+ xDataSink->setInputStream( xIn );
+ }
+ else
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedDataSinkException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ rArg.Sink ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+ }
+ }
+
+ return uno::Any();
+}
+
+
+void Content::insert( const uno::Reference< io::XInputStream >& xData,
+ sal_Int32 nNameClashResolve,
+ const uno::Reference<
+ ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+
+ OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
+ "insert command only supported by streams and folders!" );
+
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( eType == STREAM )
+ {
+ Uri aParentUri( aUri.getParentUri() );
+ OSL_ENSURE( !aParentUri.isDocument(),
+ "insert command not supported by streams that are direct "
+ "children of document root!" );
+ }
+#endif
+
+ // Check, if all required properties were set.
+ if ( eType == FOLDER )
+ {
+ // Required: Title
+
+ if ( m_aProps.getTitle().isEmpty() )
+ m_aProps.setTitle( aUri.getDecodedName() );
+ }
+ else // stream
+ {
+ // Required: data
+
+ if ( !xData.is() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::MissingInputStreamException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Required: Title
+
+ if ( m_aProps.getTitle().isEmpty() )
+ m_aProps.setTitle( aUri.getDecodedName() );
+ }
+
+ Uri aNewUri( aUri.getParentUri() + m_aProps.getTitle() );
+
+ // Handle possible name clash...
+ switch ( nNameClashResolve )
+ {
+ // fail.
+ case ucb::NameClash::ERROR:
+ if ( hasData( aNewUri ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::NameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ task::InteractionClassification_ERROR,
+ m_aProps.getTitle() ) ),
+ xEnv );
+ // Unreachable
+ }
+ break;
+
+ // replace (possibly) existing object.
+ case ucb::NameClash::OVERWRITE:
+ break;
+
+ // "invent" a new valid title.
+ case ucb::NameClash::RENAME:
+ if ( hasData( aNewUri ) )
+ {
+ sal_Int32 nTry = 0;
+
+ do
+ {
+ OUStringBuffer aNew(aNewUri.getUri());
+ aNew.append( "_" );
+ aNew.append( ++nTry );
+ aNewUri.setUri( aNew.makeStringAndClear() );
+ }
+ while ( hasData( aNewUri ) && ( nTry < 1000 ) );
+
+ if ( nTry == 1000 )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedNameClashException(
+ "Unable to resolve name clash!",
+ static_cast< cppu::OWeakObject * >( this ),
+ nNameClashResolve ) ),
+ xEnv );
+ // Unreachable
+ }
+ else
+ {
+ OUStringBuffer aNewTitle(m_aProps.getTitle());
+ aNewTitle.append( "_" );
+ aNewTitle.append( ++nTry );
+ m_aProps.setTitle( aNewTitle.makeStringAndClear() );
+ }
+ }
+ break;
+
+ case ucb::NameClash::KEEP: // deprecated
+ case ucb::NameClash::ASK:
+ default:
+ if ( hasData( aNewUri ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any(
+ ucb::UnsupportedNameClashException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ),
+ nNameClashResolve ) ),
+ xEnv );
+ // Unreachable
+ }
+ break;
+ }
+
+ // Identifier changed?
+ bool bNewId = ( aUri != aNewUri );
+
+ if ( bNewId )
+ {
+ m_xIdentifier
+ = new ::ucbhelper::ContentIdentifier( aNewUri.getUri() );
+ }
+
+ if ( !storeData( xData, xEnv ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot store persistent data!",
+ this );
+ // Unreachable
+ }
+
+ m_eState = PERSISTENT;
+
+ if ( bNewId )
+ {
+ //loadData( m_pProvider, m_aUri, m_aProps );
+
+ aGuard.clear();
+ inserted();
+ }
+}
+
+
+void Content::destroy( bool bDeletePhysical,
+ const uno::Reference<
+ ucb::XCommandEnvironment > & xEnv )
+{
+ // @@@ take care about bDeletePhysical -> trashcan support
+
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+
+ OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
+ "delete command only supported by streams and folders!" );
+
+ uno::Reference< ucb::XContent > xThis = this;
+
+ // Persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not persistent!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ m_eState = DEAD;
+
+ aGuard.clear();
+ deleted();
+
+ if ( eType == FOLDER )
+ {
+ // Process instantiated children...
+
+ ContentRefList aChildren;
+ queryChildren( aChildren );
+
+ for ( auto& rChild : aChildren )
+ {
+ rChild->destroy( bDeletePhysical, xEnv );
+ }
+ }
+}
+
+
+void Content::notifyDocumentClosed()
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ m_eState = DEAD;
+
+ // @@@ anything else to reset or such?
+
+ // callback follows!
+ aGuard.clear();
+
+ // Propagate destruction to content event listeners
+ // Remove this from provider's content list.
+ deleted();
+}
+
+
+uno::Reference< ucb::XContent >
+Content::queryChildContent( std::u16string_view rRelativeChildUri )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ const OUString aMyId = getIdentifier()->getContentIdentifier();
+ OUStringBuffer aBuf( aMyId );
+ if ( !aMyId.endsWith("/") )
+ aBuf.append( "/" );
+ if ( !o3tl::starts_with(rRelativeChildUri, u"/") )
+ aBuf.append( rRelativeChildUri );
+ else
+ aBuf.append( rRelativeChildUri.substr(1) );
+
+ uno::Reference< ucb::XContentIdentifier > xChildId
+ = new ::ucbhelper::ContentIdentifier( aBuf.makeStringAndClear() );
+
+ uno::Reference< ucb::XContent > xChild;
+ try
+ {
+ xChild = m_pProvider->queryContent( xChildId );
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // handled below.
+ }
+
+ OSL_ENSURE( xChild.is(),
+ "Content::queryChildContent - unable to create child content!" );
+ return xChild;
+}
+
+
+void Content::notifyChildRemoved( std::u16string_view rRelativeChildUri )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ // Ugly! Need to create child content object, just to fill event properly.
+ uno::Reference< ucb::XContent > xChild
+ = queryChildContent( rRelativeChildUri );
+
+ if ( !xChild.is() )
+ return;
+
+ // callback follows!
+ aGuard.clear();
+
+ // Notify "REMOVED" event.
+ ucb::ContentEvent aEvt(
+ static_cast< cppu::OWeakObject * >( this ),
+ ucb::ContentAction::REMOVED,
+ xChild,
+ getIdentifier() );
+ notifyContentEvent( aEvt );
+}
+
+
+void Content::notifyChildInserted( std::u16string_view rRelativeChildUri )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ // Ugly! Need to create child content object, just to fill event properly.
+ uno::Reference< ucb::XContent > xChild
+ = queryChildContent( rRelativeChildUri );
+
+ if ( !xChild.is() )
+ return;
+
+ // callback follows!
+ aGuard.clear();
+
+ // Notify "INSERTED" event.
+ ucb::ContentEvent aEvt(
+ static_cast< cppu::OWeakObject * >( this ),
+ ucb::ContentAction::INSERTED,
+ xChild,
+ getIdentifier() );
+ notifyContentEvent( aEvt );
+}
+
+
+void Content::transfer(
+ const ucb::TransferInfo& rInfo,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ // Persistent?
+ if ( m_eState != PERSISTENT )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::UnsupportedCommandException(
+ "Not persistent!",
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Does source URI scheme match? Only vnd.sun.star.tdoc is supported.
+
+ if ( rInfo.SourceURL.getLength() < TDOC_URL_SCHEME_LENGTH + 2 )
+ {
+ // Invalid length (to short).
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::InteractiveBadTransferURLException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ OUString aScheme
+ = rInfo.SourceURL.copy( 0, TDOC_URL_SCHEME_LENGTH + 2 )
+ .toAsciiLowerCase();
+ if ( aScheme != TDOC_URL_SCHEME ":/" )
+ {
+ // Invalid scheme.
+ ucbhelper::cancelCommandExecution(
+ uno::Any( ucb::InteractiveBadTransferURLException(
+ OUString(),
+ static_cast< cppu::OWeakObject * >( this ) ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Does source URI describe a tdoc folder or stream?
+ Uri aSourceUri( rInfo.SourceURL );
+ if ( !aSourceUri.isValid() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Invalid source URI! Syntax!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ if ( aSourceUri.isRoot() || aSourceUri.isDocument() )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Invalid source URI! Must describe a folder or stream!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+
+ // Is source not a parent of me / not me?
+ OUString aId = m_xIdentifier->getContentIdentifier();
+ sal_Int32 nPos = aId.lastIndexOf( '/' );
+ if ( nPos != ( aId.getLength() - 1 ) )
+ {
+ // No trailing slash found. Append.
+ aId += "/";
+ }
+
+ if ( rInfo.SourceURL.getLength() <= aId.getLength() )
+ {
+ if ( aId.startsWith( rInfo.SourceURL ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_RECURSIVE,
+ aArgs,
+ xEnv,
+ "Target is equal to or is a child of source!",
+ this );
+ // Unreachable
+ }
+ }
+
+ if ( m_aProps.getType() == DOCUMENT )
+ {
+ bool bOK = false;
+
+ uno::Reference< embed::XStorage > xStorage
+ = m_pProvider->queryStorage(
+ aSourceUri.getParentUri(), READ_WRITE_NOCREATE );
+ if ( xStorage.is() )
+ {
+ try
+ {
+ if ( xStorage->isStreamElement( aSourceUri.getDecodedName() ) )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Invalid source URI! "
+ "Streams cannot be created as "
+ "children of document root!",
+ static_cast< cppu::OWeakObject * >(
+ this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+ bOK = true;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // handled below.
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // handled below.
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ // handled below.
+ }
+ }
+
+ if ( !bOK )
+ {
+ ucbhelper::cancelCommandExecution(
+ uno::Any( lang::IllegalArgumentException(
+ "Invalid source URI! Unable to determine source type!",
+ static_cast< cppu::OWeakObject * >( this ),
+ -1 ) ),
+ xEnv );
+ // Unreachable
+ }
+ }
+
+
+ // Copy data.
+
+
+ OUString aNewName( !rInfo.NewTitle.isEmpty()
+ ? rInfo.NewTitle
+ : aSourceUri.getDecodedName() );
+
+ if ( !copyData( aSourceUri, aNewName ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot copy data!",
+ this );
+ // Unreachable
+ }
+
+
+ // Copy own and all children's Additional Core Properties.
+
+
+ OUString aTargetUri = m_xIdentifier->getContentIdentifier();
+ if ( ( aTargetUri.lastIndexOf( '/' ) + 1 ) != aTargetUri.getLength() )
+ aTargetUri += "/";
+
+ if ( !rInfo.NewTitle.isEmpty() )
+ aTargetUri += ::ucb_impl::urihelper::encodeSegment( rInfo.NewTitle );
+ else
+ aTargetUri += aSourceUri.getName();
+
+ if ( !copyAdditionalPropertySet( aSourceUri.getUri(), aTargetUri ) )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot copy additional properties!",
+ this );
+ // Unreachable
+ }
+
+
+ // Propagate new content.
+
+
+ rtl::Reference< Content > xTarget;
+ try
+ {
+ uno::Reference< ucb::XContentIdentifier > xTargetId
+ = new ::ucbhelper::ContentIdentifier( aTargetUri );
+
+ // Note: The static cast is okay here, because its sure that
+ // m_xProvider is always the WebDAVContentProvider.
+ xTarget = static_cast< Content * >(
+ m_pProvider->queryContent( xTargetId ).get() );
+
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+
+ if ( !xTarget.is() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(aTargetUri)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ xEnv,
+ "Cannot instantiate target object!",
+ this );
+ // Unreachable
+ }
+
+ // Announce transferred content in its new folder.
+ xTarget->inserted();
+
+
+ // Remove source, if requested
+
+
+ if ( !rInfo.MoveData )
+ return;
+
+ rtl::Reference< Content > xSource;
+ try
+ {
+ uno::Reference< ucb::XContentIdentifier >
+ xSourceId = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );
+
+ // Note: The static cast is okay here, because its sure
+ // that m_xProvider is always the ContentProvider.
+ xSource = static_cast< Content * >(
+ m_xProvider->queryContent( xSourceId ).get() );
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ // queryContent
+ }
+
+ if ( !xSource.is() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_READ,
+ aArgs,
+ xEnv,
+ "Cannot instantiate target object!",
+ this );
+ // Unreachable
+ }
+
+ // Propagate destruction (recursively).
+ xSource->destroy( true, xEnv );
+
+ // Remove all persistent data of source and its children.
+ if ( !xSource->removeData() )
+ {
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot remove persistent data of source object!",
+ this );
+ // Unreachable
+ }
+
+ // Remove own and all children's Additional Core Properties.
+ if ( xSource->removeAdditionalPropertySet() )
+ return;
+
+ uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"Uri", uno::Any(rInfo.SourceURL)}
+ }));
+ ucbhelper::cancelCommandExecution(
+ ucb::IOErrorCode_CANT_WRITE,
+ aArgs,
+ xEnv,
+ "Cannot remove additional properties of source object!",
+ this );
+ // Unreachable
+}
+
+
+//static
+bool Content::hasData( ContentProvider const * pProvider, const Uri & rUri )
+{
+ if ( rUri.isRoot() )
+ {
+ return true; // root has no storage
+ }
+ else if ( rUri.isDocument() )
+ {
+ uno::Reference< embed::XStorage > xStorage
+ = pProvider->queryStorage( rUri.getUri(), READ );
+ return xStorage.is();
+ }
+ else
+ {
+ // folder or stream
+
+ // Ask parent storage. In case that rUri describes a stream,
+ // ContentProvider::queryStorage( rUri ) would return null.
+
+ uno::Reference< embed::XStorage > xStorage
+ = pProvider->queryStorage( rUri.getParentUri(), READ );
+
+ if ( !xStorage.is() )
+ return false;
+
+ return xStorage->hasByName( rUri.getDecodedName() );
+ }
+}
+
+
+//static
+bool Content::loadData( ContentProvider const * pProvider,
+ const Uri & rUri,
+ ContentProperties& rProps )
+{
+ if ( rUri.isRoot() ) // root has no storage, but can always be created
+ {
+ rProps
+ = ContentProperties(
+ ROOT, pProvider->queryStorageTitle( rUri.getUri() ) );
+ }
+ else if ( rUri.isDocument() ) // document must have storage
+ {
+ uno::Reference< embed::XStorage > xStorage
+ = pProvider->queryStorage( rUri.getUri(), READ );
+
+ if ( !xStorage.is() )
+ return false;
+
+ rProps
+ = ContentProperties(
+ DOCUMENT, pProvider->queryStorageTitle( rUri.getUri() ) );
+ }
+ else // stream or folder; stream has no storage; folder has storage
+ {
+ uno::Reference< embed::XStorage > xStorage
+ = pProvider->queryStorage( rUri.getParentUri(), READ );
+
+ if ( !xStorage.is() )
+ return false;
+
+ // Check whether exists at all, is stream or folder
+ try
+ {
+ // return: true -> folder
+ // return: false -> stream
+ // NoSuchElementException -> neither folder nor stream
+ bool bIsFolder
+ = xStorage->isStorageElement( rUri.getDecodedName() );
+
+ rProps
+ = ContentProperties(
+ bIsFolder ? FOLDER : STREAM,
+ pProvider->queryStorageTitle( rUri.getUri() ) );
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // there is no element with such name
+ //OSL_ENSURE( false, "Caught NoSuchElementException!" );
+ return false;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // an illegal argument is provided
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ // this storage is in invalid state for any reason
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool Content::storeData( const uno::Reference< io::XInputStream >& xData,
+ const uno::Reference<
+ ucb::XCommandEnvironment >& xEnv )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
+ {
+ OSL_FAIL( "storeData not supported by root and documents!" );
+ return false;
+ }
+
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+
+ if ( eType == FOLDER )
+ {
+ uno::Reference< embed::XStorage > xStorage
+ = m_pProvider->queryStorage( aUri.getUri(), READ_WRITE_CREATE );
+
+ if ( !xStorage.is() )
+ return false;
+
+ uno::Reference< beans::XPropertySet > xPropSet(
+ xStorage, uno::UNO_QUERY );
+ OSL_ENSURE( xPropSet.is(),
+ "Content::storeData - Got no XPropertySet interface!" );
+ if ( !xPropSet.is() )
+ return false;
+
+ try
+ {
+ // According to MBA, if no mediatype is set, folder and all
+ // its contents will be lost on save of the document!!!
+ xPropSet->setPropertyValue(
+ "MediaType",
+ uno::Any(
+ OUString( // @@@ better mediatype
+ "application/binary" ) ) );
+ }
+ catch ( beans::UnknownPropertyException const & )
+ {
+ OSL_FAIL( "Property MediaType not supported!" );
+ return false;
+ }
+ catch ( beans::PropertyVetoException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+ if ( !commitStorage( xStorage ) )
+ return false;
+ }
+ else if ( eType == STREAM )
+ {
+ // stream
+
+ // Important: Parent storage and output stream must be kept alive until
+ // changes have been committed!
+ uno::Reference< embed::XStorage > xStorage
+ = m_pProvider->queryStorage(
+ aUri.getParentUri(), READ_WRITE_CREATE );
+ uno::Reference< io::XOutputStream > xOut;
+
+ if ( !xStorage.is() )
+ return false;
+
+ if ( xData.is() )
+ {
+ // May throw CommandFailedException, DocumentPasswordRequest!
+ xOut = getTruncatedOutputStream( xEnv );
+
+ OSL_ENSURE( xOut.is(), "No target data stream!" );
+
+ try
+ {
+ uno::Sequence< sal_Int8 > aBuffer;
+ while (true)
+ {
+ sal_Int32 nRead = xData->readSomeBytes( aBuffer, 65536 );
+ if (!nRead)
+ break;
+ aBuffer.realloc( nRead );
+ xOut->writeBytes( aBuffer );
+ }
+
+ closeOutputStream( xOut );
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ // readSomeBytes, writeBytes
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ closeOutputStream( xOut );
+ return false;
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ // readSomeBytes, writeBytes
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ closeOutputStream( xOut );
+ return false;
+ }
+ catch ( io::IOException const & )
+ {
+ // readSomeBytes, writeBytes
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ closeOutputStream( xOut );
+ return false;
+ }
+ catch ( ... )
+ {
+ closeOutputStream( xOut );
+ throw;
+ }
+ }
+
+ // Commit changes.
+ if ( !commitStorage( xStorage ) )
+ return false;
+ }
+ else
+ {
+ OSL_FAIL( "Unknown content type!" );
+ return false;
+ }
+ return true;
+}
+
+
+void Content::renameData(
+ const uno::Reference< ucb::XContentIdentifier >& xOldId,
+ const uno::Reference< ucb::XContentIdentifier >& xNewId )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
+ {
+ OSL_FAIL( "renameData not supported by root and documents!" );
+ return;
+ }
+
+ Uri aOldUri( xOldId->getContentIdentifier() );
+ uno::Reference< embed::XStorage > xStorage
+ = m_pProvider->queryStorage(
+ aOldUri.getParentUri(), READ_WRITE_NOCREATE );
+
+ if ( !xStorage.is() )
+ return;
+
+ try
+ {
+ Uri aNewUri( xNewId->getContentIdentifier() );
+ xStorage->renameElement(
+ aOldUri.getDecodedName(), aNewUri.getDecodedName() );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ // this storage is in invalid state for any reason
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // an illegal argument is provided
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // there is no element with old name in this storage
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+ catch ( container::ElementExistException const & )
+ {
+ // an element with new name already exists in this storage
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+ catch ( io::IOException const & )
+ {
+ // in case of io errors during renaming
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ // wraps other exceptions
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return;
+ }
+
+ commitStorage( xStorage );
+}
+
+
+bool Content::removeData()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
+ {
+ OSL_FAIL( "removeData not supported by root and documents!" );
+ return false;
+ }
+
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+ uno::Reference< embed::XStorage > xStorage
+ = m_pProvider->queryStorage(
+ aUri.getParentUri(), READ_WRITE_NOCREATE );
+
+ if ( !xStorage.is() )
+ return false;
+
+ try
+ {
+ xStorage->removeElement( aUri.getDecodedName() );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ // this storage is in invalid state for any reason
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // an illegal argument is provided
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // there is no element with this name in this storage
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( io::IOException const & )
+ {
+ // in case of io errors during renaming
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ // wraps other exceptions
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+ return commitStorage( xStorage );
+}
+
+
+bool Content::copyData( const Uri & rSourceUri, const OUString & rNewName )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ ContentType eType = m_aProps.getType();
+ if ( ( eType == ROOT ) || ( eType == STREAM ) )
+ {
+ OSL_FAIL( "copyData not supported by root and streams!" );
+ return false;
+ }
+
+ Uri aDestUri( m_xIdentifier->getContentIdentifier() );
+ uno::Reference< embed::XStorage > xDestStorage
+ = m_pProvider->queryStorage( aDestUri.getUri(), READ_WRITE_NOCREATE );
+
+ if ( !xDestStorage.is() )
+ return false;
+
+ uno::Reference< embed::XStorage > xSourceStorage
+ = m_pProvider->queryStorage( rSourceUri.getParentUri(), READ );
+
+ if ( !xSourceStorage.is() )
+ return false;
+
+ try
+ {
+ xSourceStorage->copyElementTo( rSourceUri.getDecodedName(),
+ xDestStorage,
+ rNewName );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ // this storage is in invalid state for any reason
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ // an illegal argument is provided
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ // there is no element with this name in this storage
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( container::ElementExistException const & )
+ {
+ // there is no element with this name in this storage
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( io::IOException const & )
+ {
+ // in case of io errors during renaming
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ // wraps other exceptions
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+ return commitStorage( xDestStorage );
+}
+
+
+// static
+bool Content::commitStorage( const uno::Reference< embed::XStorage > & xStorage )
+{
+ // Commit changes
+ uno::Reference< embed::XTransactedObject > xTO( xStorage, uno::UNO_QUERY );
+
+ OSL_ENSURE( xTO.is(),
+ "Required interface css.embed.XTransactedObject missing!" );
+ try
+ {
+ xTO->commit();
+ }
+ catch ( io::IOException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ return false;
+ }
+
+ return true;
+}
+
+
+// static
+bool Content::closeOutputStream(
+ const uno::Reference< io::XOutputStream > & xOut )
+{
+ if ( xOut.is() )
+ {
+ try
+ {
+ xOut->closeOutput();
+ return true;
+ }
+ catch ( io::NotConnectedException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::BufferSizeExceededException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+ return false;
+}
+
+/// @throws ucb::CommandFailedException
+/// @throws task::DocumentPasswordRequest
+static OUString obtainPassword(
+ const OUString & rName,
+ task::PasswordRequestMode eMode,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ rtl::Reference< DocumentPasswordRequest > xRequest
+ = new DocumentPasswordRequest( eMode, rName );
+
+ 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() )
+ {
+ throw ucb::CommandFailedException(
+ "Abort requested by Interaction Handler.",
+ uno::Reference< uno::XInterface >(),
+ xRequest->getRequest() );
+ }
+
+ uno::Reference< task::XInteractionPassword > xPassword(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( xPassword.is() )
+ {
+ return xPassword->getPassword();
+ }
+
+ // Unknown selection. Should never happen.
+ throw ucb::CommandFailedException(
+ "Interaction Handler selected unknown continuation!",
+ uno::Reference< uno::XInterface >(),
+ xRequest->getRequest() );
+ }
+ }
+ }
+
+ // No IH or IH did not handle exception.
+ task::DocumentPasswordRequest aRequest;
+ xRequest->getRequest() >>= aRequest;
+ throw aRequest;
+}
+
+
+uno::Reference< io::XInputStream > Content::getInputStream(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ OUString aUri;
+ OUString aPassword;
+ bool bPasswordRequested = false;
+
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ OSL_ENSURE( m_aProps.getType() == STREAM,
+ "Content::getInputStream - content is no stream!" );
+
+ aUri = Uri( m_xIdentifier->getContentIdentifier() ).getUri();
+ }
+
+ for ( ;; )
+ {
+ try
+ {
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+ return m_pProvider->queryInputStream( aUri, aPassword );
+ }
+ catch ( packages::WrongPasswordException const & )
+ {
+ // Obtain (new) password.
+ aPassword
+ = obtainPassword( aUri, /* @@@ find better title */
+ bPasswordRequested
+ ? task::PasswordRequestMode_PASSWORD_REENTER
+ : task::PasswordRequestMode_PASSWORD_ENTER,
+ xEnv );
+ bPasswordRequested = true;
+ }
+ }
+}
+
+/// @throws ucb::CommandFailedException
+/// @throws task::DocumentPasswordRequest
+/// @throws uno::RuntimeException
+static uno::Reference< io::XOutputStream > lcl_getTruncatedOutputStream(
+ const OUString & rUri,
+ ContentProvider const * pProvider,
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ OUString aPassword;
+ bool bPasswordRequested = false;
+ for ( ;; )
+ {
+ try
+ {
+ return pProvider->queryOutputStream(
+ rUri, aPassword, true /* truncate */ );
+ }
+ catch ( packages::WrongPasswordException const & )
+ {
+ // Obtain (new) password.
+ aPassword
+ = obtainPassword( rUri, /* @@@ find better title */
+ bPasswordRequested
+ ? task::PasswordRequestMode_PASSWORD_REENTER
+ : task::PasswordRequestMode_PASSWORD_ENTER,
+ xEnv );
+ bPasswordRequested = true;
+ }
+ }
+}
+
+
+uno::Reference< io::XOutputStream > Content::getTruncatedOutputStream(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ OSL_ENSURE( m_aProps.getType() == STREAM,
+ "Content::getTruncatedOutputStream - content is no stream!" );
+
+ return lcl_getTruncatedOutputStream(
+ Uri( m_xIdentifier->getContentIdentifier() ).getUri(),
+ m_pProvider,
+ xEnv );
+}
+
+
+uno::Reference< io::XStream > Content::getStream(
+ const uno::Reference< ucb::XCommandEnvironment > & xEnv )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ OSL_ENSURE( m_aProps.getType() == STREAM,
+ "Content::getStream - content is no stream!" );
+
+ OUString aUri( Uri( m_xIdentifier->getContentIdentifier() ).getUri() );
+ OUString aPassword;
+ bool bPasswordRequested = false;
+ for ( ;; )
+ {
+ try
+ {
+ return m_pProvider->queryStream(
+ aUri, aPassword, false /* no truncate */ );
+ }
+ catch ( packages::WrongPasswordException const & )
+ {
+ // Obtain (new) password.
+ aPassword
+ = obtainPassword( aUri, /* @@@ find better title */
+ bPasswordRequested
+ ? task::PasswordRequestMode_PASSWORD_REENTER
+ : task::PasswordRequestMode_PASSWORD_ENTER,
+ xEnv );
+ bPasswordRequested = true;
+ }
+ }
+}
+
+
+// ContentProperties Implementation.
+
+
+uno::Sequence< ucb::ContentInfo >
+ContentProperties::getCreatableContentsInfo() const
+{
+ if ( isContentCreator() )
+ {
+ uno::Sequence< beans::Property > aProps( 1 );
+ aProps.getArray()[ 0 ] = beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND );
+
+ if ( getType() == DOCUMENT )
+ {
+ // streams cannot be created as direct children of document root
+ uno::Sequence< ucb::ContentInfo > aSeq( 1 );
+
+ // Folder.
+ aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
+ aSeq.getArray()[ 0 ].Properties = aProps;
+
+ return aSeq;
+ }
+ else
+ {
+ uno::Sequence< ucb::ContentInfo > aSeq( 2 );
+
+ // Folder.
+ aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
+ aSeq.getArray()[ 0 ].Attributes
+ = ucb::ContentInfoAttribute::KIND_FOLDER;
+ aSeq.getArray()[ 0 ].Properties = aProps;
+
+ // Stream.
+ aSeq.getArray()[ 1 ].Type = TDOC_STREAM_CONTENT_TYPE;
+ aSeq.getArray()[ 1 ].Attributes
+ = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
+ | ucb::ContentInfoAttribute::KIND_DOCUMENT;
+ aSeq.getArray()[ 1 ].Properties = aProps;
+
+ return aSeq;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "getCreatableContentsInfo called on non-contentcreator "
+ "object!" );
+
+ return uno::Sequence< ucb::ContentInfo >( 0 );
+ }
+}
+
+
+bool ContentProperties::isContentCreator() const
+{
+ return ( getType() == FOLDER ) || ( getType() == DOCUMENT );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_content.hxx b/ucb/source/ucp/tdoc/tdoc_content.hxx
new file mode 100644
index 000000000..1412ebd9a
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_content.hxx
@@ -0,0 +1,282 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <ucbhelper/contenthelper.hxx>
+#include <com/sun/star/ucb/XContentCreator.hpp>
+#include "tdoc_provider.hxx"
+
+namespace com::sun::star {
+ namespace sdbc { class XRow; }
+ namespace io { class XInputStream; class XOutputStream; }
+ namespace beans { struct PropertyValue; }
+ namespace ucb { struct OpenCommandArgument2; struct TransferInfo;
+ struct ContentInfo; }
+}
+
+namespace tdoc_ucp
+{
+
+
+enum ContentType { STREAM, FOLDER, DOCUMENT, ROOT };
+
+class ContentProperties
+{
+public:
+ ContentProperties()
+ : m_eType( STREAM )
+ {}
+
+ ContentProperties( const ContentType & rType, const OUString & rTitle )
+ : m_eType( rType ),
+ m_aContentType( rType == STREAM
+ ? OUString( TDOC_STREAM_CONTENT_TYPE )
+ : rType == FOLDER
+ ? OUString( TDOC_FOLDER_CONTENT_TYPE )
+ : rType == DOCUMENT
+ ? OUString( TDOC_DOCUMENT_CONTENT_TYPE )
+ : OUString( TDOC_ROOT_CONTENT_TYPE ) ),
+ m_aTitle( rTitle )
+ {}
+
+ ContentType getType() const { return m_eType; }
+
+ // Properties
+
+ const OUString & getContentType() const { return m_aContentType; }
+
+ bool getIsFolder() const { return m_eType > STREAM; }
+ bool getIsDocument() const { return !getIsFolder(); }
+
+ const OUString & getTitle() const { return m_aTitle; }
+ void setTitle( const OUString & rTitle ) { m_aTitle = rTitle; }
+
+ css::uno::Sequence< css::ucb::ContentInfo >
+ getCreatableContentsInfo() const;
+
+ bool isContentCreator() const;
+
+private:
+ ContentType m_eType;
+ OUString m_aContentType;
+ OUString m_aTitle;
+};
+
+
+class Content : public ::ucbhelper::ContentImplHelper,
+ public css::ucb::XContentCreator
+{
+ enum ContentState { TRANSIENT, // created via createNewContent,
+ // but did not process "insert" yet
+ PERSISTENT, // processed "insert"
+ DEAD // processed "delete" / document was closed
+ };
+
+ ContentProperties m_aProps;
+ ContentState m_eState;
+ ContentProvider* m_pProvider;
+
+private:
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const ContentProperties & rProps );
+ Content( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const css::ucb::ContentInfo& Info );
+
+ virtual css::uno::Sequence< css::beans::Property >
+ getProperties( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual css::uno::Sequence< css::ucb::CommandInfo >
+ getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) override;
+ virtual OUString getParentURL() override;
+
+ static bool hasData( ContentProvider const * pProvider, const Uri & rUri );
+ bool hasData( const Uri & rUri ) const { return hasData( m_pProvider, rUri ); }
+
+ static bool loadData( ContentProvider const * pProvider,
+ const Uri & rUri,
+ ContentProperties& rProps );
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::task::DocumentPasswordRequest
+ /// @throws css::uno::RuntimeException
+ bool storeData( const css::uno::Reference< css::io::XInputStream >& xData,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+ void renameData( const css::uno::Reference< css::ucb::XContentIdentifier >& xOldId,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+ bool removeData();
+
+ bool copyData( const Uri & rSourceUri, const OUString & rNewName );
+
+ css::uno::Reference< css::ucb::XContentIdentifier >
+ makeNewIdentifier( const OUString& rTitle );
+
+ typedef rtl::Reference< Content > ContentRef;
+ typedef std::vector< ContentRef > ContentRefList;
+ void queryChildren( ContentRefList& rChildren );
+
+ bool exchangeIdentity(
+ const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId );
+
+ css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Sequence< css::beans::Property >& rProperties );
+ css::uno::Sequence< css::uno::Any >
+ /// @throws css::uno::Exception
+ setPropertyValues(
+ const css::uno::Sequence< css::beans::PropertyValue >& rValues,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ css::uno::Any
+ open( const css::ucb::OpenCommandArgument2& rArg,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv );
+
+ /// @throws css::uno::Exception
+ void insert( const css::uno::Reference< css::io::XInputStream >& xData,
+ sal_Int32 nNameClashResolve,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ void destroy( bool bDeletePhysical,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::uno::Exception
+ void transfer( const css::ucb::TransferInfo& rInfo,
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ static css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ const ContentProperties& rData,
+ ContentProvider* pProvider,
+ const OUString& rContentId );
+
+
+ static bool commitStorage(
+ const css::uno::Reference< css::embed::XStorage > & xStorage );
+
+ static bool closeOutputStream(
+ const css::uno::Reference< css::io::XOutputStream > & xOut );
+
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::task::DocumentPasswordRequest
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XInputStream >
+ getInputStream( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::task::DocumentPasswordRequest
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XOutputStream >
+ getTruncatedOutputStream(
+ const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+ css::uno::Reference< css::ucb::XContent >
+ queryChildContent( std::u16string_view rRelativeChildUri );
+
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::task::DocumentPasswordRequest
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XStream >
+ getStream( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv );
+
+public:
+ // Create existing content. Fail, if not already exists.
+ static rtl::Reference<Content> create(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier );
+
+ // Create new content. Fail, if already exists.
+ static rtl::Reference<Content> create(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ ContentProvider* pProvider,
+ const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
+ const css::ucb::ContentInfo& Info );
+
+ virtual ~Content() 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 css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XContent
+ virtual OUString SAL_CALL
+ getContentType() override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL
+ getIdentifier() override;
+
+ // XCommandProcessor
+ virtual css::uno::Any SAL_CALL
+ execute( const css::ucb::Command& aCommand,
+ sal_Int32 CommandId,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& Environment ) override;
+ virtual void SAL_CALL
+ abort( sal_Int32 CommandId ) override;
+
+
+ // Additional interfaces
+
+
+ // XContentCreator
+ virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL
+ queryCreatableContentsInfo() override;
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createNewContent( const css::ucb::ContentInfo& Info ) override;
+
+
+ // Non-interface methods.
+
+
+ static css::uno::Reference< css::sdbc::XRow >
+ getPropertyValues( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Sequence< css::beans::Property >& rProperties,
+ ContentProvider* pProvider,
+ const OUString& rContentId );
+
+ void notifyDocumentClosed();
+ void notifyChildRemoved( std::u16string_view rRelativeChildUri );
+ void notifyChildInserted( std::u16string_view rRelativeChildUri );
+
+ rtl::Reference< ContentProvider > getContentProvider() const
+ { return rtl::Reference< ContentProvider >( m_pProvider ); }
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_contentcaps.cxx b/ucb/source/ucp/tdoc/tdoc_contentcaps.cxx
new file mode 100644
index 000000000..8b19f9ec9
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_contentcaps.cxx
@@ -0,0 +1,622 @@
+/* -*- 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
+ **************************************************************************
+
+ **************************************************************************
+
+ Props/Commands:
+
+ root document folder folder stream stream
+ (new) (new)
+ ----------------------------------------------------------------
+ ContentType r r r r r r
+ IsDocument r r r r r r
+ IsFolder r r r r r r
+ Title r r w w w w
+ CreatableContentsInfo r r r r r r
+ Storage - - r r - -
+ DocumentModel - r - - - -
+
+ getCommandInfo x x x x x x
+ getPropertySetInfo x x x x x x
+ getPropertyValues x x x x x x
+ setPropertyValues x x x x x x
+ insert - - x x x(*) x(*)
+ delete - - x - x -
+ open x x x - x -
+ transfer - x x - - -
+ createNewContent - x x - - -
+
+
+ *************************************************************************/
+
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/ucb/CommandInfo.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+#include "tdoc_content.hxx"
+
+namespace com::sun::star::embed {
+ class XStorage;
+}
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// Content implementation.
+
+
+#define MAKEPROPSEQUENCE( a ) \
+ uno::Sequence< beans::Property >( a, SAL_N_ELEMENTS(a) )
+
+#define MAKECMDSEQUENCE( a ) \
+ uno::Sequence< ucb::CommandInfo >( a, SAL_N_ELEMENTS(a) )
+
+
+// IMPORTANT: If any property data ( name / type / ... ) are changed, then
+// Content::getPropertyValues(...) must be adapted too!
+
+
+// virtual
+uno::Sequence< beans::Property > Content::getProperties(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_aProps.getType() == STREAM )
+ {
+
+
+ // Stream: Supported properties
+
+
+ static const beans::Property aStreamPropertyInfoTable[] =
+ {
+
+ // Mandatory properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aStreamPropertyInfoTable );
+ }
+ else if ( m_aProps.getType() == FOLDER )
+ {
+
+
+ // Folder: Supported properties
+
+
+ static const beans::Property aFolderPropertyInfoTable[] =
+ {
+
+ // Mandatory properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // New properties
+
+ beans::Property(
+ "Storage",
+ -1,
+ cppu::UnoType<embed::XStorage>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ )
+ };
+ return MAKEPROPSEQUENCE( aFolderPropertyInfoTable );
+ }
+ else if ( m_aProps.getType() == DOCUMENT )
+ {
+
+
+ // Document: Supported properties
+
+
+ static const beans::Property aDocPropertyInfoTable[] =
+ {
+
+ // Mandatory properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // New properties
+
+ beans::Property(
+ "DocumentModel",
+ -1,
+ cppu::UnoType<frame::XModel>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ )
+ };
+ return MAKEPROPSEQUENCE( aDocPropertyInfoTable );
+ }
+ else
+ {
+
+
+ // Root: Supported properties
+
+
+ OSL_ENSURE( m_aProps.getType() == ROOT, "Wrong content type!" );
+
+ static const beans::Property aRootPropertyInfoTable[] =
+ {
+
+ // Mandatory properties
+
+ beans::Property(
+ "ContentType",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsDocument",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "IsFolder",
+ -1,
+ cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+ beans::Property(
+ "Title",
+ -1,
+ cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ ),
+
+ // Optional standard properties
+
+ beans::Property(
+ "CreatableContentsInfo",
+ -1,
+ cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
+ beans::PropertyAttribute::BOUND
+ | beans::PropertyAttribute::READONLY
+ )
+
+ // New properties
+
+ };
+ return MAKEPROPSEQUENCE( aRootPropertyInfoTable );
+ }
+}
+
+
+// virtual
+uno::Sequence< ucb::CommandInfo > Content::getCommands(
+ const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_aProps.getType() == STREAM )
+ {
+ Uri aUri( m_xIdentifier->getContentIdentifier() );
+ Uri aParentUri( aUri.getParentUri() );
+
+ if ( aParentUri.isDocument() )
+ {
+
+
+ // Stream, that is a child of a document: Supported commands
+
+
+ static const ucb::CommandInfo aStreamCommandInfoTable1[] =
+ {
+
+ // Mandatory commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aStreamCommandInfoTable1 );
+ }
+
+
+ // Stream: Supported commands
+
+
+ static const ucb::CommandInfo aStreamCommandInfoTable[] =
+ {
+
+ // Mandatory commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType< uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aStreamCommandInfoTable );
+ }
+ else if ( m_aProps.getType() == FOLDER )
+ {
+
+
+ // Folder: Supported commands
+
+
+ static const ucb::CommandInfo aFolderCommandInfoTable[] =
+ {
+
+ // Mandatory commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "delete",
+ -1,
+ cppu::UnoType<bool>::get()
+ ),
+ ucb::CommandInfo(
+ "insert",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aFolderCommandInfoTable );
+ }
+ else if ( m_aProps.getType() == DOCUMENT )
+ {
+
+
+ // Document: Supported commands
+
+
+ static const ucb::CommandInfo aDocCommandInfoTable[] =
+ {
+
+ // Mandatory commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ ),
+ ucb::CommandInfo(
+ "transfer",
+ -1,
+ cppu::UnoType<ucb::TransferInfo>::get()
+ ),
+ ucb::CommandInfo(
+ "createNewContent",
+ -1,
+ cppu::UnoType<ucb::ContentInfo>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aDocCommandInfoTable );
+ }
+ else
+ {
+
+
+ // Root: Supported commands
+
+
+ OSL_ENSURE( m_aProps.getType() == ROOT, "Wrong content type!" );
+
+ static const ucb::CommandInfo aRootCommandInfoTable[] =
+ {
+
+ // Mandatory commands
+
+ ucb::CommandInfo(
+ "getCommandInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertySetInfo",
+ -1,
+ cppu::UnoType<void>::get()
+ ),
+ ucb::CommandInfo(
+ "getPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::Property >>::get()
+ ),
+ ucb::CommandInfo(
+ "setPropertyValues",
+ -1,
+ cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get()
+ ),
+
+ // Optional standard commands
+
+ ucb::CommandInfo(
+ "open",
+ -1,
+ cppu::UnoType<ucb::OpenCommandArgument2>::get()
+ )
+
+ // New commands
+
+ };
+ return MAKECMDSEQUENCE( aRootCommandInfoTable );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_datasupplier.cxx b/ucb/source/ucp/tdoc/tdoc_datasupplier.cxx
new file mode 100644
index 000000000..8873e1da2
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_datasupplier.cxx
@@ -0,0 +1,380 @@
+/* -*- 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 <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <com/sun/star/ucb/ResultSetException.hpp>
+#include <osl/diagnose.h>
+#include <ucbhelper/contentidentifier.hxx>
+
+#include "tdoc_datasupplier.hxx"
+#include "tdoc_content.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+namespace tdoc_ucp
+{
+
+
+// struct ResultListEntry.
+
+namespace {
+
+
+}
+
+// struct DataSupplier_Impl.
+
+
+}
+
+// DataSupplier Implementation.
+ResultSetDataSupplier::ResultSetDataSupplier(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent )
+: m_xContent( rContent ), m_xContext( rxContext ),
+ m_bCountFinal( false ), m_bThrowException( false )
+{
+}
+
+// virtual
+ResultSetDataSupplier::~ResultSetDataSupplier()
+{
+}
+
+// virtual
+OUString
+ResultSetDataSupplier::queryContentIdentifierString( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ OUString aId = m_aResults[ nIndex ].aURL;
+ if ( !aId.isEmpty() )
+ {
+ // Already cached.
+ return aId;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ // Note: getResult fills m_pImpl->m_aResults[ nIndex ]->aURL.
+ return m_aResults[ nIndex ].aURL;
+ }
+ return OUString();
+}
+
+// virtual
+uno::Reference< ucb::XContentIdentifier >
+ResultSetDataSupplier::queryContentIdentifier( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = m_aResults[ nIndex ].xId;
+ if ( xId.is() )
+ {
+ // Already cached.
+ return xId;
+ }
+ }
+
+ OUString aId = queryContentIdentifierString( nIndex );
+ if ( !aId.isEmpty() )
+ {
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aId );
+ m_aResults[ nIndex ].xId = xId;
+ return xId;
+ }
+ return uno::Reference< ucb::XContentIdentifier >();
+}
+
+// virtual
+uno::Reference< ucb::XContent >
+ResultSetDataSupplier::queryContent( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_aResults[ nIndex ].xContent;
+ if ( xContent.is() )
+ {
+ // Already cached.
+ return xContent;
+ }
+ }
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = queryContentIdentifier( nIndex );
+ if ( xId.is() )
+ {
+ try
+ {
+ uno::Reference< ucb::XContent > xContent
+ = m_xContent->getProvider()->queryContent( xId );
+ m_aResults[ nIndex ].xContent = xContent;
+ return xContent;
+
+ }
+ catch ( ucb::IllegalIdentifierException const & )
+ {
+ }
+ }
+ return uno::Reference< ucb::XContent >();
+}
+
+// virtual
+bool ResultSetDataSupplier::getResult( sal_uInt32 nIndex )
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_aResults.size() > nIndex )
+ {
+ // Result already present.
+ return true;
+ }
+
+ // Result not (yet) present.
+
+ if ( m_bCountFinal )
+ return false;
+
+ // Try to obtain result...
+
+ sal_uInt32 nOldCount = m_aResults.size();
+ bool bFound = false;
+
+ if ( queryNamesOfChildren() )
+ {
+ for ( sal_uInt32 n = nOldCount;
+ n < sal::static_int_cast<sal_uInt32>(
+ m_xNamesOfChildren->getLength());
+ ++n )
+ {
+ const OUString & rName
+ = m_xNamesOfChildren->getConstArray()[ n ];
+
+ if ( rName.isEmpty() )
+ {
+ OSL_FAIL( "ResultDataSupplier::getResult - Empty name!" );
+ break;
+ }
+
+ // Assemble URL for child.
+ OUString aURL = assembleChildURL( rName );
+
+ m_aResults.emplace_back( aURL );
+
+ if ( n == nIndex )
+ {
+ // Result obtained.
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if ( !bFound )
+ m_bCountFinal = true;
+
+ rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet();
+ if ( xResultSet.is() )
+ {
+ // Callbacks follow!
+ aGuard.clear();
+
+ if ( nOldCount < m_aResults.size() )
+ xResultSet->rowCountChanged( nOldCount, m_aResults.size() );
+
+ if ( m_bCountFinal )
+ xResultSet->rowCountFinal();
+ }
+
+ return bFound;
+}
+
+// virtual
+sal_uInt32 ResultSetDataSupplier::totalCount()
+{
+ osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( m_bCountFinal )
+ return m_aResults.size();
+
+ sal_uInt32 nOldCount = m_aResults.size();
+
+ if ( queryNamesOfChildren() )
+ {
+ for ( sal_uInt32 n = nOldCount;
+ n < sal::static_int_cast<sal_uInt32>(
+ m_xNamesOfChildren->getLength());
+ ++n )
+ {
+ const OUString & rName
+ = m_xNamesOfChildren->getConstArray()[ n ];
+
+ if ( rName.isEmpty() )
+ {
+ OSL_FAIL( "ResultDataSupplier::getResult - Empty name!" );
+ break;
+ }
+
+ // Assemble URL for child.
+ OUString aURL = assembleChildURL( rName );
+
+ m_aResults.emplace_back( aURL );
+ }
+ }
+
+ m_bCountFinal = true;
+
+ rtl::Reference< ::ucbhelper::ResultSet > xResultSet = getResultSet();
+ if ( xResultSet.is() )
+ {
+ // Callbacks follow!
+ aGuard.clear();
+
+ if ( nOldCount < m_aResults.size() )
+ xResultSet->rowCountChanged( nOldCount, m_aResults.size() );
+
+ xResultSet->rowCountFinal();
+ }
+
+ return m_aResults.size();
+}
+
+// virtual
+sal_uInt32 ResultSetDataSupplier::currentCount()
+{
+ return m_aResults.size();
+}
+
+// virtual
+bool ResultSetDataSupplier::isCountFinal()
+{
+ return m_bCountFinal;
+}
+
+// virtual
+uno::Reference< sdbc::XRow >
+ResultSetDataSupplier::queryPropertyValues( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ {
+ uno::Reference< sdbc::XRow > xRow = m_aResults[ nIndex ].xRow;
+ if ( xRow.is() )
+ {
+ // Already cached.
+ return xRow;
+ }
+ }
+
+ if ( getResult( nIndex ) )
+ {
+ uno::Reference< sdbc::XRow > xRow = Content::getPropertyValues(
+ m_xContext,
+ getResultSet()->getProperties(),
+ m_xContent->getContentProvider().get(),
+ queryContentIdentifierString( nIndex ) );
+ m_aResults[ nIndex ].xRow = xRow;
+ return xRow;
+ }
+
+ return uno::Reference< sdbc::XRow >();
+}
+
+// virtual
+void ResultSetDataSupplier::releasePropertyValues( sal_uInt32 nIndex )
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( nIndex < m_aResults.size() )
+ m_aResults[ nIndex ].xRow.clear();
+}
+
+// virtual
+void ResultSetDataSupplier::close()
+{
+}
+
+// virtual
+void ResultSetDataSupplier::validate()
+{
+ if ( m_bThrowException )
+ throw ucb::ResultSetException();
+}
+
+bool ResultSetDataSupplier::queryNamesOfChildren()
+{
+ osl::Guard< osl::Mutex > aGuard( m_aMutex );
+
+ if ( !m_xNamesOfChildren )
+ {
+ uno::Sequence< OUString > aNamesOfChildren;
+
+ if ( !m_xContent->getContentProvider()->queryNamesOfChildren(
+ m_xContent->getIdentifier()->getContentIdentifier(),
+ aNamesOfChildren ) )
+ {
+ OSL_FAIL( "Got no list of children!" );
+ m_bThrowException = true;
+ return false;
+ }
+ else
+ {
+ m_xNamesOfChildren = std::move( aNamesOfChildren );
+ }
+ }
+ return true;
+}
+
+OUString
+ResultSetDataSupplier::assembleChildURL( std::u16string_view aName )
+{
+ OUString aContURL
+ = m_xContent->getIdentifier()->getContentIdentifier();
+ OUString aURL( aContURL );
+
+ sal_Int32 nUrlEnd = aURL.lastIndexOf( '/' );
+ if ( nUrlEnd != aURL.getLength() - 1 )
+ aURL += "/";
+
+ aURL += aName;
+ return aURL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_datasupplier.hxx b/ucb/source/ucp/tdoc/tdoc_datasupplier.hxx
new file mode 100644
index 000000000..e9f138002
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_datasupplier.hxx
@@ -0,0 +1,86 @@
+/* -*- 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/ref.hxx>
+#include <ucbhelper/resultset.hxx>
+#include <optional>
+#include <vector>
+#include <string_view>
+
+namespace tdoc_ucp {
+
+struct DataSupplier_Impl;
+class Content;
+
+class ResultSetDataSupplier : public ::ucbhelper::ResultSetDataSupplier
+{
+ struct ResultListEntry
+ {
+ OUString aURL;
+ css::uno::Reference< css::ucb::XContentIdentifier > xId;
+ css::uno::Reference< css::ucb::XContent > xContent;
+ css::uno::Reference< css::sdbc::XRow > xRow;
+
+ explicit ResultListEntry( const OUString& rURL ) : aURL( rURL ) {}
+ };
+
+ osl::Mutex m_aMutex;
+ std::vector< ResultListEntry > m_aResults;
+ rtl::Reference< Content > m_xContent;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ std::optional<css::uno::Sequence< OUString > > m_xNamesOfChildren;
+ bool m_bCountFinal;
+ bool m_bThrowException;
+
+private:
+ bool queryNamesOfChildren();
+ OUString assembleChildURL( std::u16string_view aName );
+
+public:
+ ResultSetDataSupplier(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rContent );
+ virtual ~ResultSetDataSupplier() override;
+
+ virtual OUString queryContentIdentifierString( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContentIdentifier >
+ queryContentIdentifier( sal_uInt32 nIndex ) override;
+ virtual css::uno::Reference< css::ucb::XContent >
+ queryContent( sal_uInt32 nIndex ) override;
+
+ virtual bool getResult( sal_uInt32 nIndex ) override;
+
+ virtual sal_uInt32 totalCount() override;
+ virtual sal_uInt32 currentCount() override;
+ virtual bool isCountFinal() override;
+
+ virtual css::uno::Reference< css::sdbc::XRow >
+ queryPropertyValues( sal_uInt32 nIndex ) override;
+ virtual void releasePropertyValues( sal_uInt32 nIndex ) override;
+
+ virtual void close() override;
+
+ virtual void validate() override;
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_docmgr.cxx b/ucb/source/ucp/tdoc/tdoc_docmgr.cxx
new file mode 100644
index 000000000..1ecc77520
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_docmgr.cxx
@@ -0,0 +1,657 @@
+/* -*- 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 <rtl/ref.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <comphelper/documentinfo.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+
+#include "tdoc_docmgr.hxx"
+#include "tdoc_provider.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+// OfficeDocumentsCloseListener Implementation.
+
+
+// util::XCloseListener
+
+
+// virtual
+void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
+ const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ )
+{
+}
+
+
+void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
+ const lang::EventObject& Source )
+{
+ if (!m_pManager) return; // disposed?
+
+ document::DocumentEvent aDocEvent;
+ aDocEvent.Source = Source.Source;
+ aDocEvent.EventName = "OfficeDocumentsListener::notifyClosing";
+ m_pManager->documentEventOccured( aDocEvent );
+}
+
+
+// lang::XDocumentEventListener (base of util::XCloseListener)
+
+
+// virtual
+void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
+ const lang::EventObject& /*Source*/ )
+{
+}
+
+
+// OfficeDocumentsManager Implementation.
+
+
+OfficeDocumentsManager::OfficeDocumentsManager(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ ContentProvider * pDocEventListener )
+: m_xContext( rxContext ),
+ m_xDocEvtNotifier( frame::theGlobalEventBroadcaster::get( rxContext ) ),
+ m_pDocEventListener( pDocEventListener ),
+ m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
+{
+ // Order is important (multithreaded environment)
+ uno::Reference< document::XDocumentEventBroadcaster >(
+ m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->addDocumentEventListener( this );
+ buildDocumentsList();
+}
+
+
+// virtual
+OfficeDocumentsManager::~OfficeDocumentsManager()
+{
+ //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
+ // no need to assert this: Normal shutdown of LibreOffice could already trigger it, since the order
+ // in which objects are actually released/destroyed upon shutdown is not defined. And when we
+ // arrive *here*, LibreOffice *is* shutting down currently, since we're held by the TDOC provider,
+ // which is disposed upon shutdown.
+ m_xDocCloseListener->Dispose();
+}
+
+
+void OfficeDocumentsManager::destroy()
+{
+ uno::Reference< document::XDocumentEventBroadcaster >(
+ m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->removeDocumentEventListener( this );
+}
+
+
+static OUString
+getDocumentId( const uno::Reference< uno::XInterface > & xDoc )
+{
+ OUString aId;
+
+ // Try to get the UID directly from the document.
+ uno::Reference< beans::XPropertySet > xPropSet( xDoc, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ uno::Any aValue = xPropSet->getPropertyValue("RuntimeUID");
+ aValue >>= aId;
+ }
+ catch ( beans::UnknownPropertyException const & )
+ {
+ // Not actually an error. Property is optional.
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "Caught WrappedTargetException!");
+ }
+ }
+
+ if ( aId.isEmpty() )
+ {
+ // fallback: generate UID from document's this pointer.
+ // normalize the interface pointer first. Else, calls with different
+ // interfaces to the same object (say, XFoo and XBar) will produce
+ // different IDs
+ uno::Reference< uno::XInterface > xNormalizedIFace( xDoc, uno::UNO_QUERY );
+ sal_Int64 nId = reinterpret_cast< sal_Int64 >( xNormalizedIFace.get() );
+ aId = OUString::number( nId );
+ }
+
+ OSL_ENSURE( !aId.isEmpty(), "getDocumentId - Empty id!" );
+ return aId;
+}
+
+
+// document::XDocumentEventListener
+
+
+// virtual
+void SAL_CALL OfficeDocumentsManager::documentEventOccured(
+ const document::DocumentEvent & Event )
+{
+/*
+ Events documentation: OOo Developer's Guide / Writing UNO Components /
+ Integrating Components into OpenOffice.org / Jobs
+*/
+
+ if ( Event.EventName == "OnLoadFinished" // document loaded
+ || Event.EventName == "OnCreate" ) // document created
+ {
+ if ( isOfficeDocument( Event.Source ) )
+ {
+ uno::Reference<frame::XModel> const xModel(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
+
+ bool found(false);
+
+ {
+ std::scoped_lock aGuard( m_aMtx );
+
+ found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+ }
+
+ if (!found)
+ {
+ // no mutex to avoid deadlocks!
+ // need no lock to access const members, ContentProvider is safe
+
+ // new document
+
+ uno::Reference< document::XStorageBasedDocument >
+ xDoc( Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xDoc.is(), "Got no document::XStorageBasedDocument!" );
+
+ uno::Reference< embed::XStorage > xStorage
+ = xDoc->getDocumentStorage();
+ OSL_ENSURE( xStorage.is(), "Got no document storage!" );
+
+ rtl:: OUString aDocId = getDocumentId( Event.Source );
+ rtl:: OUString aTitle = comphelper::DocumentInfo::getDocumentTitle(
+ uno::Reference< frame::XModel >( Event.Source, uno::UNO_QUERY ) );
+
+ {
+ std::scoped_lock g(m_aMtx);
+ m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
+ }
+
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xCloseBroadcaster.is(),
+ "OnLoadFinished/OnCreate event: got no close broadcaster!" );
+
+ if ( xCloseBroadcaster.is() )
+ xCloseBroadcaster->addCloseListener(m_xDocCloseListener);
+
+ // Propagate document closure.
+ OSL_ENSURE( m_pDocEventListener,
+ "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
+
+ if ( m_pDocEventListener )
+ m_pDocEventListener->notifyDocumentOpened( aDocId );
+ }
+ }
+ }
+ else if ( Event.EventName == "OfficeDocumentsListener::notifyClosing" )
+ {
+ if ( isOfficeDocument( Event.Source ) )
+ {
+ // Document has been closed (unloaded)
+
+ // Official event "OnUnload" does not work here. Event
+ // gets fired too early. Other OnUnload listeners called after this
+ // listener may still need TDOC access to the document. Remove the
+ // document from TDOC docs list on XCloseListener::notifyClosing.
+ // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
+
+ uno::Reference< frame::XModel >
+ xModel( Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
+
+ bool found(false);
+ OUString aDocId;
+
+ {
+ std::scoped_lock aGuard( m_aMtx );
+
+ auto it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+ if ( it != m_aDocs.end() )
+ {
+ aDocId = (*it).first;
+ found = true;
+ m_aDocs.erase( it );
+ }
+ }
+
+ OSL_ENSURE( found,
+ "OnUnload event notified for unknown document!" );
+
+ if (found)
+ {
+ // Propagate document closure.
+ OSL_ENSURE( m_pDocEventListener,
+ "OnUnload event: no owner for close event propagation!" );
+ if (m_pDocEventListener)
+ {
+ m_pDocEventListener->notifyDocumentClosed(aDocId);
+ }
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xCloseBroadcaster.is(),
+ "OnUnload event: got no XCloseBroadcaster from XModel" );
+ if ( xCloseBroadcaster.is() )
+ xCloseBroadcaster->removeCloseListener(m_xDocCloseListener);
+ }
+ }
+ }
+ else if ( Event.EventName == "OnSaveDone" )
+ {
+ if ( isOfficeDocument( Event.Source ) )
+ {
+ // Storage gets exchanged while saving.
+ uno::Reference<document::XStorageBasedDocument> const xDoc(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xDoc.is(),
+ "Got no document::XStorageBasedDocument!" );
+ uno::Reference<embed::XStorage> const xStorage(
+ xDoc->getDocumentStorage());
+ OSL_ENSURE( xStorage.is(), "Got no document storage!" );
+
+ uno::Reference< frame::XModel >
+ xModel( Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
+
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+
+ OSL_ENSURE( it != m_aDocs.end(),
+ "OnSaveDone event notified for unknown document!" );
+ if ( it != m_aDocs.end() )
+ {
+ (*it).second.xStorage = xStorage;
+ }
+ }
+ }
+ else if ( Event.EventName == "OnSaveAsDone" )
+ {
+ if ( isOfficeDocument( Event.Source ) )
+ {
+ // Storage gets exchanged while saving.
+ uno::Reference<document::XStorageBasedDocument> const xDoc(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xDoc.is(),
+ "Got no document::XStorageBasedDocument!" );
+ uno::Reference<embed::XStorage> const xStorage(
+ xDoc->getDocumentStorage());
+ OSL_ENSURE( xStorage.is(), "Got no document storage!" );
+
+ uno::Reference< frame::XModel >
+ xModel( Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
+
+ OUString const title(comphelper::DocumentInfo::getDocumentTitle(xModel));
+
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+
+ OSL_ENSURE( it != m_aDocs.end(),
+ "OnSaveAsDone event notified for unknown document!" );
+ if ( it != m_aDocs.end() )
+ {
+ (*it).second.xStorage = xStorage;
+
+ // Adjust title.
+ (*it).second.aTitle = title;
+ }
+ }
+ }
+ else if ( Event.EventName == "OnTitleChanged"
+ || Event.EventName == "OnStorageChanged" )
+ {
+ if ( isOfficeDocument( Event.Source ) )
+ {
+ // Storage gets exchanged while saving.
+ uno::Reference<document::XStorageBasedDocument> const xDoc(
+ Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xDoc.is(),
+ "Got no document::XStorageBasedDocument!" );
+ uno::Reference<embed::XStorage> const xStorage(
+ xDoc->getDocumentStorage());
+ OSL_ENSURE( xStorage.is(), "Got no document storage!" );
+
+ uno::Reference< frame::XModel >
+ xModel( Event.Source, uno::UNO_QUERY );
+ OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
+
+ OUString const aTitle(comphelper::DocumentInfo::getDocumentTitle(xModel));
+
+ OUString const aDocId(getDocumentId(Event.Source));
+
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+ if ( it != m_aDocs.end() )
+ {
+ // Adjust title.
+ (*it).second.aTitle = aTitle;
+
+ m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
+ }
+
+// OSL_ENSURE( it != m_aDocs.end(),
+// "TitleChanged event notified for unknown document!" );
+ // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
+ // and the fix is more difficult than what can be done now.
+ // The problem is that at the moment, when you close a SFX-based document via API, it will first
+ // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
+ // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
+ // *after* OnUnload and all other On* events.
+ // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
+ // as last event. When this happens, the assertion here must be enabled, again.
+ }
+ }
+}
+
+// lang::XDocumentEventListener (base of document::XDocumentEventListener)
+
+// virtual
+void SAL_CALL OfficeDocumentsManager::disposing(
+ const lang::EventObject& /*Source*/ )
+{
+}
+
+// Non-interface.
+
+void OfficeDocumentsManager::buildDocumentsList()
+{
+ uno::Reference< container::XEnumeration > xEnum
+ = m_xDocEvtNotifier->createEnumeration();
+
+ while ( xEnum->hasMoreElements() )
+ {
+ uno::Any aValue = xEnum->nextElement();
+ // container::NoSuchElementException
+ // lang::WrappedTargetException
+
+ try
+ {
+ uno::Reference< frame::XModel > xModel;
+ aValue >>= xModel;
+
+ if ( xModel.is() )
+ {
+ if ( isOfficeDocument( xModel ) )
+ {
+ bool found(false);
+
+ {
+ std::scoped_lock aGuard( m_aMtx );
+
+ found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
+ [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
+ }
+
+ if (!found)
+ {
+ // new document
+ OUString aDocId = getDocumentId( xModel );
+ OUString aTitle = comphelper::DocumentInfo::getDocumentTitle( xModel );
+
+ uno::Reference< document::XStorageBasedDocument >
+ xDoc( xModel, uno::UNO_QUERY );
+ OSL_ENSURE( xDoc.is(),
+ "Got no document::XStorageBasedDocument!" );
+
+ uno::Reference< embed::XStorage > xStorage
+ = xDoc->getDocumentStorage();
+ OSL_ENSURE( xStorage.is(), "Got no document storage!" );
+
+ {
+ std::scoped_lock aGuard( m_aMtx );
+ m_aDocs[ aDocId ]
+ = StorageInfo( aTitle, xStorage, xModel );
+ }
+
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
+ xModel, uno::UNO_QUERY );
+ OSL_ENSURE( xCloseBroadcaster.is(),
+ "buildDocumentsList: got no close broadcaster!" );
+
+ if ( xCloseBroadcaster.is() )
+ xCloseBroadcaster->addCloseListener(m_xDocCloseListener);
+ }
+ }
+ }
+ }
+ catch ( lang::DisposedException const & )
+ {
+ // Note: Due to race conditions the XEnumeration can
+ // contain docs that have already been closed
+ }
+ catch ( lang::NotInitializedException const & )
+ {
+ // Note: Due to race conditions the XEnumeration can
+ // contain docs that are still uninitialized
+ }
+ }
+}
+
+uno::Reference< embed::XStorage >
+OfficeDocumentsManager::queryStorage( const OUString & rDocId )
+{
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::const_iterator it = m_aDocs.find( rDocId );
+ if ( it == m_aDocs.end() )
+ return uno::Reference< embed::XStorage >();
+
+ return (*it).second.xStorage;
+}
+
+
+OUString OfficeDocumentsManager::queryDocumentId(
+ const uno::Reference< frame::XModel > & xModel )
+{
+ return getDocumentId( xModel );
+}
+
+
+uno::Reference< frame::XModel >
+OfficeDocumentsManager::queryDocumentModel( const OUString & rDocId )
+{
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::const_iterator it = m_aDocs.find( rDocId );
+ if ( it == m_aDocs.end() )
+ return uno::Reference< frame::XModel >();
+
+ return (*it).second.xModel;
+}
+
+
+uno::Sequence< OUString > OfficeDocumentsManager::queryDocuments()
+{
+ std::scoped_lock aGuard( m_aMtx );
+
+ return comphelper::mapKeysToSequence( m_aDocs );
+}
+
+
+OUString
+OfficeDocumentsManager::queryStorageTitle( const OUString & rDocId )
+{
+ std::scoped_lock aGuard( m_aMtx );
+
+ DocumentList::const_iterator it = m_aDocs.find( rDocId );
+ if ( it == m_aDocs.end() )
+ return OUString();
+
+ return (*it).second.aTitle;
+}
+
+
+bool OfficeDocumentsManager::isDocumentPreview(
+ const uno::Reference< frame::XModel > & xModel )
+{
+ if ( !xModel.is() )
+ return false;
+
+ bool bIsPreview = ::comphelper::NamedValueCollection::getOrDefault( xModel->getArgs(), u"Preview", false );
+ return bIsPreview;
+}
+
+
+bool OfficeDocumentsManager::isHelpDocument(
+ const uno::Reference< frame::XModel > & xModel )
+{
+ if ( !xModel.is() )
+ return false;
+
+ OUString sURL( xModel->getURL() );
+ return sURL.match( "vnd.sun.star.help://" );
+}
+
+
+bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
+ const uno::Reference< frame::XModel > & xModel )
+{
+ if ( !xModel.is() )
+ return false;
+
+ uno::Reference< frame::XController > xController
+ = xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ uno::Reference< frame::XFrame > xFrame
+ = xController->getFrame();
+ if ( xFrame.is() )
+ {
+ // don't use XFrame::isTop here. This nowadays excludes
+ // "sub documents" such as forms embedded in database documents
+ uno::Reference< awt::XTopWindow > xFrameContainer(
+ xFrame->getContainerWindow(), uno::UNO_QUERY );
+ if ( !xFrameContainer.is() )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool OfficeDocumentsManager::isBasicIDE(
+ const uno::Reference< frame::XModel > & xModel )
+{
+ if ( !m_xModuleMgr.is() )
+ {
+ std::scoped_lock aGuard( m_aMtx );
+ if ( !m_xModuleMgr.is() )
+ {
+ try
+ {
+ m_xModuleMgr = frame::ModuleManager::create( m_xContext );
+ }
+ catch ( uno::Exception const & )
+ {
+ // handled below.
+ }
+
+ OSL_ENSURE( m_xModuleMgr .is(),
+ "Could not instantiate ModuleManager service!" );
+ }
+ }
+
+ if ( m_xModuleMgr.is() )
+ {
+ OUString aModule;
+ try
+ {
+ aModule = m_xModuleMgr->identify( xModel );
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( frame::UnknownModuleException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ if ( !aModule.isEmpty() )
+ {
+ // Filter unwanted items, that are no real documents.
+ if ( aModule == "com.sun.star.script.BasicIDE" )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+bool OfficeDocumentsManager::isOfficeDocument(
+ const uno::Reference< uno::XInterface > & xDoc )
+{
+ uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
+ uno::Reference< document::XStorageBasedDocument >
+ xStorageBasedDoc( xModel, uno::UNO_QUERY );
+ if ( !xStorageBasedDoc.is() )
+ return false;
+
+ if ( !isWithoutOrInTopLevelFrame( xModel ) )
+ return false;
+
+ if ( isDocumentPreview( xModel ) )
+ return false;
+
+ if ( isHelpDocument( xModel ) )
+ return false;
+
+ if ( isBasicIDE( xModel ) )
+ return false;
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_docmgr.hxx b/ucb/source/ucp/tdoc/tdoc_docmgr.hxx
new file mode 100644
index 000000000..37839f985
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_docmgr.hxx
@@ -0,0 +1,151 @@
+/* -*- 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/ref.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XModuleManager2.hpp>
+#include <com/sun/star/frame/XGlobalEventBroadcaster.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/util/XCloseListener.hpp>
+
+#include <map>
+#include <mutex>
+
+namespace tdoc_ucp {
+
+ class ContentProvider;
+
+ struct StorageInfo
+ {
+ OUString aTitle;
+ css::uno::Reference< css::embed::XStorage > xStorage;
+ css::uno::Reference< css::frame::XModel > xModel;
+
+ StorageInfo() {}; // needed for STL map only.
+
+ StorageInfo(
+ const OUString & rTitle,
+ const css::uno::Reference< css::embed::XStorage > & rxStorage,
+ const css::uno::Reference< css::frame::XModel > & rxModel )
+ : aTitle( rTitle ), xStorage( rxStorage ), xModel( rxModel ) {}
+ };
+
+
+ typedef std::map< OUString, StorageInfo > DocumentList;
+
+
+ class OfficeDocumentsManager :
+ public cppu::WeakImplHelper< css::document::XDocumentEventListener >
+ {
+ class OfficeDocumentsCloseListener :
+ public cppu::WeakImplHelper< css::util::XCloseListener >
+
+ {
+ public:
+ explicit OfficeDocumentsCloseListener( OfficeDocumentsManager * pMgr )
+ : m_pManager( pMgr ) {}
+
+ // util::XCloseListener
+ virtual void SAL_CALL queryClosing(
+ const css::lang::EventObject& Source,
+ sal_Bool GetsOwnership ) override;
+
+ virtual void SAL_CALL notifyClosing(
+ const css::lang::EventObject& Source ) override;
+
+ // lang::XEventListener (base of util::XCloseListener)
+ virtual void SAL_CALL disposing(
+ const css::lang::EventObject & Source ) override;
+
+ void Dispose() { m_pManager = nullptr; }
+
+ private:
+ OfficeDocumentsManager * m_pManager;
+ };
+
+ public:
+ OfficeDocumentsManager(
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ ContentProvider * pDocEventListener );
+ virtual ~OfficeDocumentsManager() override;
+
+ void destroy();
+
+ // document::XDocumentEventListener
+ virtual void SAL_CALL documentEventOccured(
+ const css::document::DocumentEvent & Event ) override;
+
+ // lang::XEventListener (base of document::XDocumentEventListener)
+ virtual void SAL_CALL disposing(
+ const css::lang::EventObject & Source ) override;
+
+ // Non-interface
+ css::uno::Reference< css::embed::XStorage >
+ queryStorage( const OUString & rDocId );
+
+ static OUString queryDocumentId(
+ const css::uno::Reference< css::frame::XModel > & xModel );
+
+ css::uno::Reference< css::frame::XModel >
+ queryDocumentModel( const OUString & rDocId );
+
+ css::uno::Sequence< OUString >
+ queryDocuments();
+
+ OUString
+ queryStorageTitle( const OUString & rDocId );
+
+ private:
+ void buildDocumentsList();
+
+ bool isOfficeDocument(
+ const css::uno::Reference< css::uno::XInterface > & xDoc );
+
+ static bool isDocumentPreview(
+ const css::uno::Reference< css::frame::XModel > & xModel );
+
+ static bool isWithoutOrInTopLevelFrame(
+ const css::uno::Reference< css::frame::XModel > & xModel );
+
+ bool
+ isBasicIDE(
+ const css::uno::Reference< css::frame::XModel > & xModel );
+
+ static bool isHelpDocument(
+ const css::uno::Reference< css::frame::XModel > & xModel );
+
+ std::mutex m_aMtx;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XGlobalEventBroadcaster > m_xDocEvtNotifier;
+ css::uno::Reference< css::frame::XModuleManager2 > m_xModuleMgr;
+ DocumentList m_aDocs;
+ ContentProvider * const m_pDocEventListener;
+ ::rtl::Reference<OfficeDocumentsCloseListener> const m_xDocCloseListener;
+ };
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.cxx b/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.cxx
new file mode 100644
index 000000000..cd40d6d0e
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.cxx
@@ -0,0 +1,114 @@
+/* -*- 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 <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+
+#include "tdoc_documentcontentfactory.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// DocumentContentFactory Implementation.
+
+
+DocumentContentFactory::DocumentContentFactory(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+: m_xContext( rxContext )
+{
+}
+
+
+// virtual
+DocumentContentFactory::~DocumentContentFactory()
+{
+}
+
+
+// XServiceInfo methods.
+
+
+// virtual
+OUString SAL_CALL DocumentContentFactory::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.TransientDocumentsDocumentContentFactory";
+}
+
+// virtual
+sal_Bool SAL_CALL
+DocumentContentFactory::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// virtual
+uno::Sequence< OUString > SAL_CALL
+DocumentContentFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.TransientDocumentsDocumentContentFactory" };
+}
+
+
+// XTransientDocumentsDocumentContentFactory methods.
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+DocumentContentFactory::createDocumentContent(
+ const uno::Reference< frame::XModel >& Model )
+{
+ uno::Reference< frame::XTransientDocumentsDocumentContentFactory > xDocFac;
+ try
+ {
+ xDocFac.set( m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.ucb.TransientDocumentsContentProvider", m_xContext),
+ uno::UNO_QUERY );
+ }
+ catch ( uno::Exception const & )
+ {
+ // handled below.
+ }
+
+ if ( xDocFac.is() )
+ return xDocFac->createDocumentContent( Model );
+
+ throw uno::RuntimeException(
+ "Unable to obtain document content factory!",
+ static_cast< cppu::OWeakObject * >( this ) );
+}
+
+
+// Service factory implementation.
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_tdoc_DocumentContentFactory_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new DocumentContentFactory(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.hxx b/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.hxx
new file mode 100644
index 000000000..bcbeee7d8
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_documentcontentfactory.hxx
@@ -0,0 +1,58 @@
+/* -*- 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/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+namespace tdoc_ucp {
+
+class DocumentContentFactory :
+ public cppu::WeakImplHelper<
+ css::frame::XTransientDocumentsDocumentContentFactory,
+ css::lang::XServiceInfo >
+{
+public:
+ explicit DocumentContentFactory( const css::uno::Reference< css::uno::XComponentContext >& );
+ virtual ~DocumentContentFactory() 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;
+
+ // XTransientDocumentsDocumentContentFactory
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createDocumentContent( const css::uno::Reference< css::frame::XModel >& Model ) override;
+
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_passwordrequest.cxx b/ucb/source/ucp/tdoc/tdoc_passwordrequest.cxx
new file mode 100644
index 000000000..412b39fe8
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_passwordrequest.cxx
@@ -0,0 +1,191 @@
+/* -*- 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 <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/task/DocumentPasswordRequest.hpp>
+#include <com/sun/star/task/XInteractionPassword.hpp>
+
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <ucbhelper/interactionrequest.hxx>
+
+#include "tdoc_passwordrequest.hxx"
+
+#include <mutex>
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+namespace tdoc_ucp
+{
+ namespace {
+
+ class InteractionSupplyPassword :
+ public ucbhelper::InteractionContinuation,
+ public lang::XTypeProvider,
+ public task::XInteractionPassword
+ {
+ public:
+ explicit InteractionSupplyPassword( ucbhelper::InteractionRequest * pRequest )
+ : InteractionContinuation( pRequest ) {}
+
+ // XInterface
+ virtual uno::Any SAL_CALL queryInterface( const uno::Type & rType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider
+ virtual uno::Sequence< uno::Type > SAL_CALL getTypes() override;
+ virtual uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ // XInteractionContinuation
+ virtual void SAL_CALL select() override;
+
+ // XInteractionPassword
+ virtual void SAL_CALL setPassword( const OUString & aPasswd ) override;
+ virtual OUString SAL_CALL getPassword() override;
+
+ private:
+ std::mutex m_aMutex;
+ OUString m_aPassword;
+ };
+
+ }
+} // namespace tdoc_ucp
+
+
+// InteractionSupplyPassword Implementation.
+
+
+// XInterface methods.
+
+
+// virtual
+void SAL_CALL InteractionSupplyPassword::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+// virtual
+void SAL_CALL InteractionSupplyPassword::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+
+// virtual
+uno::Any SAL_CALL
+InteractionSupplyPassword::queryInterface( const uno::Type & rType )
+{
+ uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XTypeProvider * >( this ),
+ static_cast< task::XInteractionContinuation * >( this ),
+ static_cast< task::XInteractionPassword * >( this ) );
+
+ return aRet.hasValue()
+ ? aRet : InteractionContinuation::queryInterface( rType );
+}
+
+
+// XTypeProvider methods.
+
+
+// virtual
+uno::Sequence< sal_Int8 > SAL_CALL
+InteractionSupplyPassword::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL InteractionSupplyPassword::getTypes()
+{
+ static cppu::OTypeCollection s_aCollection(
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<task::XInteractionPassword>::get() );
+
+ return s_aCollection.getTypes();
+}
+
+
+// XInteractionContinuation methods.
+
+
+// virtual
+void SAL_CALL InteractionSupplyPassword::select()
+{
+ recordSelection();
+}
+
+
+// XInteractionPassword methods.
+
+
+// virtual
+void SAL_CALL
+InteractionSupplyPassword::setPassword( const OUString& aPasswd )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ m_aPassword = aPasswd;
+}
+
+// virtual
+OUString SAL_CALL InteractionSupplyPassword::getPassword()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ return m_aPassword;
+}
+
+
+// DocumentPasswordRequest Implementation.
+
+
+DocumentPasswordRequest::DocumentPasswordRequest(
+ task::PasswordRequestMode eMode,
+ const OUString & rDocumentName )
+{
+ // Fill request...
+ task::DocumentPasswordRequest aRequest;
+// aRequest.Message = // OUString
+// aRequest.Context = // XInterface
+ aRequest.Classification = task::InteractionClassification_ERROR;
+ aRequest.Mode = eMode;
+ aRequest.Name = rDocumentName;
+
+ setRequest( uno::Any( aRequest ) );
+
+ // Fill continuations...
+ uno::Sequence<
+ uno::Reference< task::XInteractionContinuation > > aContinuations{
+ new ucbhelper::InteractionAbort( this ),
+ new ucbhelper::InteractionRetry( this ),
+ new InteractionSupplyPassword( this )
+ };
+
+ setContinuations( aContinuations );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_passwordrequest.hxx b/ucb/source/ucp/tdoc/tdoc_passwordrequest.hxx
new file mode 100644
index 000000000..d246a5ea4
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_passwordrequest.hxx
@@ -0,0 +1,87 @@
+/* -*- 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/task/PasswordRequestMode.hpp>
+
+#include <ucbhelper/interactionrequest.hxx>
+
+namespace tdoc_ucp
+{
+/*
+ @usage:
+
+ uno::Reference< ucb::XCommandEnvironment > Environment = ...;
+
+ if ( Environment.is() )
+ {
+ uno::Reference< task::XInteractionHandler > xIH
+ = Environment->getInteractionHandler();
+ if ( xIH.is() )
+ {
+ rtl::Reference< DocumentPasswordRequest > xRequest
+ = new DocumentPasswordRequest(
+ task::PasswordRequestMode_PASSWORD_ENTER,
+ m_xIdentifier->getContentIdentifier() );
+ xIH->handle( xRequest.get() );
+
+ 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() )
+ {
+ // @@@
+ }
+
+ uno::Reference< task::XInteractionRetry > xRetry(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( xRetry.is() )
+ {
+ // @@@
+ }
+
+ uno::Reference< task::XInteractionPassword > xPassword(
+ xSelection.get(), uno::UNO_QUERY );
+ if ( xPassword.is() )
+ {
+ OUString aPassword = xPassword->getPassword();
+
+ // @@@
+ }
+ }
+ }
+ }
+
+ */
+
+class DocumentPasswordRequest : public ucbhelper::InteractionRequest
+{
+public:
+ DocumentPasswordRequest(css::task::PasswordRequestMode eMode, const OUString& rDocumentName);
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_provider.cxx b/ucb/source/ucp/tdoc/tdoc_provider.cxx
new file mode 100644
index 000000000..23fd324a5
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_provider.cxx
@@ -0,0 +1,605 @@
+/* -*- 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 <tools/diagnose_ex.h>
+
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/macros.hxx>
+
+#include "tdoc_provider.hxx"
+#include "tdoc_content.hxx"
+#include "tdoc_uri.hxx"
+#include "tdoc_docmgr.hxx"
+#include "tdoc_storage.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// ContentProvider Implementation.
+
+
+ContentProvider::ContentProvider(
+ const uno::Reference< uno::XComponentContext >& rxContext )
+: ::ucbhelper::ContentProviderImplHelper( rxContext ),
+ m_xDocsMgr( new OfficeDocumentsManager( rxContext, this ) ),
+ m_xStgElemFac( new StorageElementFactory( rxContext, m_xDocsMgr ) )
+{
+}
+
+
+// virtual
+ContentProvider::~ContentProvider()
+{
+ if ( m_xDocsMgr.is() )
+ m_xDocsMgr->destroy();
+}
+
+
+// XInterface methods.
+void SAL_CALL ContentProvider::acquire()
+ noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ContentProvider::release()
+ noexcept
+{
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL ContentProvider::queryInterface( const css::uno::Type & rType )
+{
+ css::uno::Any aRet = cppu::queryInterface( rType,
+ static_cast< lang::XTypeProvider* >(this),
+ static_cast< lang::XServiceInfo* >(this),
+ static_cast< ucb::XContentProvider* >(this),
+ static_cast< frame::XTransientDocumentsDocumentContentIdentifierFactory* >(this),
+ static_cast< frame::XTransientDocumentsDocumentContentFactory* >(this)
+ );
+ return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
+}
+
+// XTypeProvider methods.
+
+
+XTYPEPROVIDER_IMPL_5( ContentProvider,
+ lang::XTypeProvider,
+ lang::XServiceInfo,
+ ucb::XContentProvider,
+ frame::XTransientDocumentsDocumentContentIdentifierFactory,
+ frame::XTransientDocumentsDocumentContentFactory );
+
+
+// XServiceInfo methods.
+OUString SAL_CALL ContentProvider::getImplementationName()
+{
+ return "com.sun.star.comp.ucb.TransientDocumentsContentProvider";
+}
+
+sal_Bool SAL_CALL ContentProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+css::uno::Sequence< OUString > SAL_CALL ContentProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.ucb.TransientDocumentsContentProvider" };
+}
+
+
+// Service factory implementation.
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ucb_tdoc_ContentProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ContentProvider(context));
+}
+
+// XContentProvider methods.
+
+
+// virtual
+uno::Reference< ucb::XContent > SAL_CALL
+ContentProvider::queryContent(
+ const uno::Reference< ucb::XContentIdentifier >& Identifier )
+{
+ Uri aUri( Identifier->getContentIdentifier() );
+ if ( !aUri.isValid() )
+ throw ucb::IllegalIdentifierException(
+ "Invalid URL!",
+ Identifier );
+
+ // Normalize URI.
+ uno::Reference< ucb::XContentIdentifier > xCanonicId
+ = new ::ucbhelper::ContentIdentifier( aUri.getUri() );
+
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xCanonicId );
+
+ if ( !xContent.is() )
+ {
+ // Create a new content.
+ xContent = Content::create( m_xContext, this, xCanonicId );
+ registerNewContent( xContent );
+ }
+
+ return xContent;
+}
+
+
+// XTransientDocumentsDocumentContentIdentifierFactory methods.
+
+uno::Reference<ucb::XContentIdentifier> SAL_CALL
+ContentProvider::createDocumentContentIdentifier(
+ uno::Reference<frame::XModel> const& xModel)
+{
+ // model -> id -> content identifier -> queryContent
+ if ( !m_xDocsMgr.is() )
+ {
+ throw lang::IllegalArgumentException(
+ "No Document Manager!",
+ static_cast< cppu::OWeakObject * >( this ),
+ 1 );
+ }
+
+ OUString aDocId = tdoc_ucp::OfficeDocumentsManager::queryDocumentId(xModel);
+ if ( aDocId.isEmpty() )
+ {
+ throw lang::IllegalArgumentException(
+ "Unable to obtain document id from model!",
+ static_cast< cppu::OWeakObject * >( this ),
+ 1 );
+ }
+
+ OUString aBuffer = TDOC_URL_SCHEME ":/" + aDocId;
+
+ uno::Reference< ucb::XContentIdentifier > xId
+ = new ::ucbhelper::ContentIdentifier( aBuffer );
+ return xId;
+}
+
+// XTransientDocumentsDocumentContentFactory methods.
+
+uno::Reference< ucb::XContent > SAL_CALL
+ContentProvider::createDocumentContent(
+ uno::Reference<frame::XModel> const& xModel)
+{
+ uno::Reference<ucb::XContentIdentifier> const xId(
+ createDocumentContentIdentifier(xModel));
+
+ osl::MutexGuard aGuard( m_aMutex );
+
+ // Check, if a content with given id already exists...
+ uno::Reference< ucb::XContent > xContent
+ = queryExistingContent( xId );
+
+ if ( !xContent.is() )
+ {
+ // Create a new content.
+ xContent = Content::create( m_xContext, this, xId );
+ }
+
+ if ( xContent.is() )
+ return xContent;
+
+ // no content.
+ throw lang::IllegalArgumentException(
+ "Illegal Content Identifier!",
+ static_cast< cppu::OWeakObject * >( this ),
+ 1 );
+}
+
+
+// interface OfficeDocumentsEventListener
+
+
+// virtual
+void ContentProvider::notifyDocumentClosed( std::u16string_view rDocId )
+{
+ osl::MutexGuard aGuard( getContentListMutex() );
+
+ ::ucbhelper::ContentRefList aAllContents;
+ queryExistingContents( aAllContents );
+
+ // Notify all content objects related to the closed doc.
+
+ bool bFoundDocumentContent = false;
+ rtl::Reference< Content > xRoot;
+
+ for ( const auto& rContent : aAllContents )
+ {
+ Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
+ OSL_ENSURE( aUri.isValid(),
+ "ContentProvider::notifyDocumentClosed - Invalid URI!" );
+
+ if ( !bFoundDocumentContent )
+ {
+ if ( aUri.isRoot() )
+ {
+ xRoot = static_cast< Content * >( rContent.get() );
+ }
+ else if ( aUri.isDocument() )
+ {
+ if ( aUri.getDocumentId() == rDocId )
+ {
+ bFoundDocumentContent = true;
+
+ // document content will notify removal of child itself;
+ // no need for the root to propagate this.
+ xRoot.clear();
+ }
+ }
+ }
+
+ if ( aUri.getDocumentId() == rDocId )
+ {
+ // Inform content.
+ rtl::Reference< Content > xContent
+ = static_cast< Content * >( rContent.get() );
+
+ xContent->notifyDocumentClosed();
+ }
+ }
+
+ if ( xRoot.is() )
+ {
+ // No document content found for rDocId but root content
+ // instantiated. Root content must announce document removal
+ // to content event listeners.
+ xRoot->notifyChildRemoved( rDocId );
+ }
+}
+
+
+// virtual
+void ContentProvider::notifyDocumentOpened( std::u16string_view rDocId )
+{
+ osl::MutexGuard aGuard( getContentListMutex() );
+
+ ::ucbhelper::ContentRefList aAllContents;
+ queryExistingContents( aAllContents );
+
+ // Find root content. If instantiated let it propagate document insertion.
+
+ for ( const auto& rContent : aAllContents )
+ {
+ Uri aUri( rContent->getIdentifier()->getContentIdentifier() );
+ OSL_ENSURE( aUri.isValid(),
+ "ContentProvider::notifyDocumentOpened - Invalid URI!" );
+
+ if ( aUri.isRoot() )
+ {
+ rtl::Reference< Content > xRoot
+ = static_cast< Content * >( rContent.get() );
+ xRoot->notifyChildInserted( rDocId );
+
+ // Done.
+ break;
+ }
+ }
+}
+
+
+// Non-UNO
+
+
+uno::Reference< embed::XStorage >
+ContentProvider::queryStorage( const OUString & rUri,
+ StorageAccessMode eMode ) const
+{
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ return m_xStgElemFac->createStorage( rUri, eMode );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ // Okay to happen, for instance when the storage does not exist.
+ //OSL_ENSURE( false, "Caught IOException!" );
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+ return uno::Reference< embed::XStorage >();
+}
+
+
+uno::Reference< embed::XStorage >
+ContentProvider::queryStorageClone( const OUString & rUri ) const
+{
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ Uri aUri( rUri );
+ uno::Reference< embed::XStorage > xParentStorage
+ = m_xStgElemFac->createStorage( aUri.getParentUri(), READ );
+ uno::Reference< embed::XStorage > xStorage
+ = m_xStgElemFac->createTemporaryStorage();
+
+ xParentStorage->copyStorageElementLastCommitTo(
+ aUri.getDecodedName(), xStorage );
+ return xStorage;
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ // Okay to happen, for instance when the storage does not exist.
+ //OSL_ENSURE( false, "Caught IOException!" );
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+
+ return uno::Reference< embed::XStorage >();
+}
+
+
+uno::Reference< io::XInputStream >
+ContentProvider::queryInputStream( const OUString & rUri,
+ const OUString & rPassword ) const
+{
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ return m_xStgElemFac->createInputStream( rUri, rPassword );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+// catch ( packages::WrongPasswordException const & )
+// {
+// // the key provided is wrong; rethrow; to be handled by caller.
+// throw;
+// }
+ }
+ return uno::Reference< io::XInputStream >();
+}
+
+
+uno::Reference< io::XOutputStream >
+ContentProvider::queryOutputStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate ) const
+{
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ return
+ m_xStgElemFac->createOutputStream( rUri, rPassword, bTruncate );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ // Okay to happen, for instance when the storage does not exist.
+ //OSL_ENSURE( false, "Caught IOException!" );
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+// catch ( packages::WrongPasswordException const & )
+// {
+// // the key provided is wrong; rethrow; to be handled by caller.
+// throw;
+// }
+ }
+ return uno::Reference< io::XOutputStream >();
+}
+
+
+uno::Reference< io::XStream >
+ContentProvider::queryStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate ) const
+{
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ return m_xStgElemFac->createStream( rUri, rPassword, bTruncate );
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ // Okay to happen, for instance when the storage does not exist.
+ //OSL_ENSURE( false, "Caught IOException!" );
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+// catch ( packages::WrongPasswordException const & )
+// {
+// // the key provided is wrong; rethrow; to be handled by caller.
+// throw;
+// }
+ }
+ return uno::Reference< io::XStream >();
+}
+
+
+bool ContentProvider::queryNamesOfChildren(
+ const OUString & rUri, uno::Sequence< OUString > & rNames ) const
+{
+ Uri aUri( rUri );
+ if ( aUri.isRoot() )
+ {
+ // special handling for root, which has no storage, but children.
+ if ( m_xDocsMgr.is() )
+ {
+ rNames = m_xDocsMgr->queryDocuments();
+ return true;
+ }
+ }
+ else
+ {
+ if ( m_xStgElemFac.is() )
+ {
+ try
+ {
+ uno::Reference< embed::XStorage > xStorage
+ = m_xStgElemFac->createStorage( rUri, READ );
+
+ OSL_ENSURE( xStorage.is(), "Got no Storage!" );
+
+ if ( xStorage.is() )
+ {
+ rNames = xStorage->getElementNames();
+ return true;
+ }
+ }
+ catch ( embed::InvalidStorageException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( lang::IllegalArgumentException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ catch ( io::IOException const & )
+ {
+ // Okay to happen, for instance if the storage does not exist.
+ //OSL_ENSURE( false, "Caught IOException!" );
+ }
+ catch ( embed::StorageWrappedTargetException const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+ }
+ }
+ return false;
+}
+
+
+OUString
+ContentProvider::queryStorageTitle( const OUString & rUri ) const
+{
+ OUString aTitle;
+
+ Uri aUri( rUri );
+ if ( aUri.isRoot() )
+ {
+ // always empty.
+ aTitle.clear();
+ }
+ else if ( aUri.isDocument() )
+ {
+ // for documents, title shall not be derived from URL. It shall
+ // be something more 'speaking' than just the document UID.
+ if ( m_xDocsMgr.is() )
+ aTitle = m_xDocsMgr->queryStorageTitle( aUri.getDocumentId() );
+ }
+ else
+ {
+ // derive title from URL
+ aTitle = aUri.getDecodedName();
+ }
+
+ OSL_ENSURE( !aTitle.isEmpty() || aUri.isRoot(),
+ "ContentProvider::queryStorageTitle - empty title!" );
+ return aTitle;
+}
+
+
+uno::Reference< frame::XModel >
+ContentProvider::queryDocumentModel( const OUString & rUri ) const
+{
+ uno::Reference< frame::XModel > xModel;
+
+ if ( m_xDocsMgr.is() )
+ {
+ Uri aUri( rUri );
+ xModel = m_xDocsMgr->queryDocumentModel( aUri.getDocumentId() );
+ }
+
+ OSL_ENSURE( xModel.is(),
+ "ContentProvider::queryDocumentModel - no model!" );
+ return xModel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_provider.hxx b/ucb/source/ucp/tdoc/tdoc_provider.hxx
new file mode 100644
index 000000000..95ffa789f
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_provider.hxx
@@ -0,0 +1,144 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/ref.hxx>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentIdentifierFactory.hpp>
+#include <ucbhelper/providerhelper.hxx>
+#include "tdoc_uri.hxx"
+#include "tdoc_docmgr.hxx"
+#include "tdoc_storage.hxx"
+
+namespace com::sun::star::embed {
+ class XStorage;
+}
+
+namespace com::sun::star::frame {
+ class XModel;
+}
+
+namespace tdoc_ucp {
+
+
+inline constexpr OUStringLiteral TDOC_ROOT_CONTENT_TYPE =
+ u"application/" TDOC_URL_SCHEME "-root";
+inline constexpr OUStringLiteral TDOC_DOCUMENT_CONTENT_TYPE =
+ u"application/" TDOC_URL_SCHEME "-document";
+inline constexpr OUStringLiteral TDOC_FOLDER_CONTENT_TYPE =
+ u"application/" TDOC_URL_SCHEME "-folder";
+inline constexpr OUStringLiteral TDOC_STREAM_CONTENT_TYPE =
+ u"application/" TDOC_URL_SCHEME "-stream";
+
+
+class StorageElementFactory;
+
+class ContentProvider
+ : public ::ucbhelper::ContentProviderImplHelper
+ , public css::frame::XTransientDocumentsDocumentContentIdentifierFactory
+ , public css::frame::XTransientDocumentsDocumentContentFactory
+{
+public:
+ explicit ContentProvider( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ContentProvider() 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;
+
+ // XContentProvider
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ queryContent( const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier ) override;
+
+ // XTransientDocumentsDocumentContentIdentifierFactory
+ virtual css::uno::Reference<css::ucb::XContentIdentifier> SAL_CALL
+ createDocumentContentIdentifier(
+ css::uno::Reference<css::frame::XModel> const& xModel) override;
+
+ // XTransientDocumentsDocumentContentFactory
+ virtual css::uno::Reference< css::ucb::XContent > SAL_CALL
+ createDocumentContent( const css::uno::Reference<
+ css::frame::XModel >& Model ) override;
+
+ // Non-UNO interfaces
+ css::uno::Reference< css::embed::XStorage >
+ queryStorage( const OUString & rUri, StorageAccessMode eMode ) const;
+
+ css::uno::Reference< css::embed::XStorage >
+ queryStorageClone( const OUString & rUri ) const;
+
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XInputStream >
+ queryInputStream( const OUString & rUri,
+ const OUString & rPassword ) const;
+
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XOutputStream >
+ queryOutputStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate ) const;
+
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XStream >
+ queryStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate ) const;
+
+ bool queryNamesOfChildren(
+ const OUString & rUri,
+ css::uno::Sequence< OUString > & rNames ) const;
+
+ // storage properties
+ OUString queryStorageTitle( const OUString & rUri ) const;
+
+ css::uno::Reference< css::frame::XModel >
+ queryDocumentModel( const OUString & rUri ) const;
+
+ // interface OfficeDocumentsEventListener
+ void notifyDocumentOpened( std::u16string_view rDocId );
+ void notifyDocumentClosed( std::u16string_view rDocId );
+
+private:
+ rtl::Reference< OfficeDocumentsManager > m_xDocsMgr;
+ rtl::Reference< StorageElementFactory > m_xStgElemFac;
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_resultset.cxx b/ucb/source/ucp/tdoc/tdoc_resultset.cxx
new file mode 100644
index 000000000..c94e5191d
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_resultset.cxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/**************************************************************************
+ TODO
+ **************************************************************************
+
+ - This implementation is not a dynamic result set!!! It only implements
+ the necessary interfaces, but never recognizes/notifies changes!!!
+
+ *************************************************************************/
+
+#include <ucbhelper/resultset.hxx>
+
+#include "tdoc_datasupplier.hxx"
+#include "tdoc_resultset.hxx"
+#include "tdoc_content.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// DynamicResultSet Implementation.
+
+
+DynamicResultSet::DynamicResultSet(
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const ucb::OpenCommandArgument2& rCommand )
+: ResultSetImplHelper( rxContext, rCommand ),
+ m_xContent( rxContent )
+{
+}
+
+
+// Non-interface methods.
+
+
+void DynamicResultSet::initStatic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet(
+ m_xContext,
+ m_aCommand.Properties,
+ new ResultSetDataSupplier( m_xContext,
+ m_xContent ) );
+}
+
+
+void DynamicResultSet::initDynamic()
+{
+ m_xResultSet1
+ = new ::ucbhelper::ResultSet(
+ m_xContext,
+ m_aCommand.Properties,
+ new ResultSetDataSupplier( m_xContext,
+ m_xContent ) );
+ m_xResultSet2 = m_xResultSet1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_resultset.hxx b/ucb/source/ucp/tdoc/tdoc_resultset.hxx
new file mode 100644
index 000000000..d0fbe418b
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_resultset.hxx
@@ -0,0 +1,47 @@
+/* -*- 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/ref.hxx>
+#include <ucbhelper/resultsethelper.hxx>
+#include "tdoc_content.hxx"
+
+namespace tdoc_ucp {
+
+class Content;
+
+class DynamicResultSet : public ::ucbhelper::ResultSetImplHelper
+{
+ rtl::Reference< Content > m_xContent;
+
+private:
+ virtual void initStatic() override;
+ virtual void initDynamic() override;
+
+public:
+ DynamicResultSet(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const rtl::Reference< Content >& rxContent,
+ const css::ucb::OpenCommandArgument2& rCommand );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_stgelems.cxx b/ucb/source/ucp/tdoc/tdoc_stgelems.cxx
new file mode 100644
index 000000000..ad1ac498d
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_stgelems.cxx
@@ -0,0 +1,876 @@
+/* -*- 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
+ **************************************************************************
+
+ - remove root storage access workaround
+
+ *************************************************************************/
+
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/reflection/ProxyFactory.hpp>
+
+#include "tdoc_uri.hxx"
+
+#include "tdoc_stgelems.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// ParentStorageHolder Implementation.
+
+
+ParentStorageHolder::ParentStorageHolder(
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const OUString & rUri )
+: m_xParentStorage( xParentStorage ),
+ m_bParentIsRootStorage( false )
+{
+ Uri aUri( rUri );
+ if ( aUri.isDocument() )
+ m_bParentIsRootStorage = true;
+}
+
+
+// Storage Implementation.
+
+
+Storage::Storage( const uno::Reference< uno::XComponentContext > & rxContext,
+ const rtl::Reference< StorageElementFactory > & xFactory,
+ const OUString & rUri,
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const uno::Reference< embed::XStorage > & xStorageToWrap )
+: ParentStorageHolder( xParentStorage, Uri( rUri ).getParentUri() ),
+ m_xFactory( xFactory ),
+ m_xWrappedStorage( xStorageToWrap ),
+ m_xWrappedTransObj( xStorageToWrap, uno::UNO_QUERY ), // optional interface
+ m_xWrappedComponent( xStorageToWrap ),
+ m_xWrappedTypeProv( xStorageToWrap, uno::UNO_QUERY ),
+ m_bIsDocumentStorage( Uri( rUri ).isDocument() )
+{
+ OSL_ENSURE( m_xWrappedStorage.is(),
+ "Storage::Storage: No storage to wrap!" );
+
+ OSL_ENSURE( m_xWrappedComponent.is(),
+ "Storage::Storage: No component to wrap!" );
+
+ OSL_ENSURE( m_xWrappedTypeProv.is(),
+ "Storage::Storage: No Type Provider!" );
+
+ // Use proxy factory service to create aggregatable proxy.
+ try
+ {
+ uno::Reference< reflection::XProxyFactory > xProxyFac =
+ reflection::ProxyFactory::create( rxContext );
+ m_xAggProxy = xProxyFac->createProxy( m_xWrappedStorage );
+ }
+ catch ( uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ OSL_ENSURE( m_xAggProxy.is(),
+ "Storage::Storage: Wrapped storage cannot be aggregated!" );
+
+ if ( !m_xAggProxy.is() )
+ return;
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // Solaris compiler problem:
+ // Extra block to enforce destruction of temporary object created
+ // in next statement _before_ osl_atomic_decrement is
+ // called. Otherwise 'this' will destroy itself even before ctor
+ // is completed (See impl. of XInterface::release())!
+
+ m_xAggProxy->setDelegator(
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+// virtual
+Storage::~Storage()
+{
+ if ( m_xAggProxy.is() )
+ m_xAggProxy->setDelegator( uno::Reference< uno::XInterface >() );
+
+ // Never dispose a document storage. Not owner!
+ if ( m_bIsDocumentStorage )
+ return;
+
+ if ( !m_xWrappedComponent.is() )
+ return;
+
+ // "Auto-dispose"...
+ try
+ {
+ m_xWrappedComponent->dispose();
+ }
+ catch ( lang::DisposedException const & )
+ {
+ // might happen.
+ }
+ catch ( ... )
+ {
+ TOOLS_WARN_EXCEPTION( "ucb", "Storage::~Storage - Caught exception!" );
+ }
+}
+
+
+// uno::XInterface
+
+
+// virtual
+uno::Any SAL_CALL Storage::queryInterface( const uno::Type& aType )
+{
+ // First, try to use interfaces implemented by myself and base class(es)
+ uno::Any aRet = StorageUNOBase::queryInterface( aType );
+
+ if ( aRet.hasValue() )
+ return aRet;
+
+ // Try to use requested interface from aggregated storage
+ return m_xAggProxy->queryAggregation( aType );
+}
+
+
+// virtual
+void SAL_CALL Storage::acquire()
+ noexcept
+{
+ osl_atomic_increment( &m_refCount );
+}
+
+
+// virtual
+void SAL_CALL Storage::release()
+ noexcept
+{
+ //#i120738, Storage::release overrides OWeakObject::release(),
+ //need call OWeakObject::release() to release OWeakObject::m_pWeakConnectionPoint
+
+ if ( m_refCount == 1 )
+ m_xFactory->releaseElement( this );
+
+ //delete this;
+ OWeakObject::release();
+}
+
+
+// lang::XTypeProvider
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Storage::getTypes()
+{
+ return m_xWrappedTypeProv->getTypes();
+}
+
+
+// virtual
+uno::Sequence< sal_Int8 > SAL_CALL Storage::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+// lang::XComponent (base of embed::XStorage)
+
+
+// virtual
+void SAL_CALL Storage::dispose()
+{
+ m_xWrappedStorage->dispose();
+ m_xWrappedStorage.clear();
+}
+
+
+// virtual
+void SAL_CALL Storage::addEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ m_xWrappedStorage->addEventListener( xListener );
+}
+
+// virtual
+void SAL_CALL Storage::removeEventListener(
+ const uno::Reference< lang::XEventListener >& aListener )
+{
+ m_xWrappedStorage->removeEventListener( aListener );
+}
+
+
+// container::XElementAccess (base of container::XNameAccess)
+
+
+// virtual
+uno::Type SAL_CALL Storage::getElementType()
+{
+ return m_xWrappedStorage->getElementType();
+}
+
+
+// virtual
+sal_Bool SAL_CALL Storage::hasElements()
+{
+ return m_xWrappedStorage->hasElements();
+}
+
+
+// container::XNameAccess (base of embed::XStorage)
+
+
+// virtual
+uno::Any SAL_CALL Storage::getByName( const OUString& aName )
+{
+ return m_xWrappedStorage->getByName( aName );
+}
+
+
+// virtual
+uno::Sequence< OUString > SAL_CALL Storage::getElementNames()
+{
+ return m_xWrappedStorage->getElementNames();
+}
+
+
+// virtual
+sal_Bool SAL_CALL Storage::hasByName( const OUString& aName )
+{
+ return m_xWrappedStorage->hasByName( aName );
+}
+
+
+// embed::XStorage
+
+
+// virtual
+void SAL_CALL Storage::copyToStorage(
+ const uno::Reference< embed::XStorage >& xDest )
+{
+ m_xWrappedStorage->copyToStorage( xDest );
+}
+
+
+// virtual
+uno::Reference< io::XStream > SAL_CALL Storage::openStreamElement(
+ const OUString& aStreamName, sal_Int32 nOpenMode )
+{
+ return m_xWrappedStorage->openStreamElement( aStreamName, nOpenMode );
+}
+
+
+// virtual
+uno::Reference< io::XStream > SAL_CALL Storage::openEncryptedStreamElement(
+ const OUString& aStreamName,
+ sal_Int32 nOpenMode,
+ const OUString& aPassword )
+{
+ return m_xWrappedStorage->openEncryptedStreamElement(
+ aStreamName, nOpenMode, aPassword );
+}
+
+
+// virtual
+uno::Reference< embed::XStorage > SAL_CALL Storage::openStorageElement(
+ const OUString& aStorName, sal_Int32 nOpenMode )
+{
+ return m_xWrappedStorage->openStorageElement( aStorName, nOpenMode );
+}
+
+
+// virtual
+uno::Reference< io::XStream > SAL_CALL Storage::cloneStreamElement(
+ const OUString& aStreamName )
+{
+ return m_xWrappedStorage->cloneStreamElement( aStreamName );
+}
+
+
+// virtual
+uno::Reference< io::XStream > SAL_CALL Storage::cloneEncryptedStreamElement(
+ const OUString& aStreamName,
+ const OUString& aPassword )
+{
+ return m_xWrappedStorage->cloneEncryptedStreamElement( aStreamName,
+ aPassword );
+}
+
+
+// virtual
+void SAL_CALL Storage::copyLastCommitTo(
+ const uno::Reference< embed::XStorage >& xTargetStorage )
+{
+ m_xWrappedStorage->copyLastCommitTo( xTargetStorage );
+}
+
+
+// virtual
+void SAL_CALL Storage::copyStorageElementLastCommitTo(
+ const OUString& aStorName,
+ const uno::Reference< embed::XStorage >& xTargetStorage )
+{
+ m_xWrappedStorage->copyStorageElementLastCommitTo( aStorName, xTargetStorage );
+}
+
+
+// virtual
+sal_Bool SAL_CALL Storage::isStreamElement(
+ const OUString& aElementName )
+{
+ return m_xWrappedStorage->isStreamElement( aElementName );
+}
+
+
+// virtual
+sal_Bool SAL_CALL Storage::isStorageElement(
+ const OUString& aElementName )
+{
+ return m_xWrappedStorage->isStorageElement( aElementName );
+}
+
+
+// virtual
+void SAL_CALL Storage::removeElement( const OUString& aElementName )
+{
+ m_xWrappedStorage->removeElement( aElementName );
+}
+
+
+// virtual
+void SAL_CALL Storage::renameElement( const OUString& aEleName,
+ const OUString& aNewName )
+{
+ m_xWrappedStorage->renameElement( aEleName, aNewName );
+}
+
+
+// virtual
+void SAL_CALL Storage::copyElementTo(
+ const OUString& aElementName,
+ const uno::Reference< embed::XStorage >& xDest,
+ const OUString& aNewName )
+{
+ m_xWrappedStorage->copyElementTo( aElementName, xDest, aNewName );
+}
+
+
+// virtual
+void SAL_CALL Storage::moveElementTo(
+ const OUString& aElementName,
+ const uno::Reference< embed::XStorage >& xDest,
+ const OUString& rNewName )
+{
+ m_xWrappedStorage->moveElementTo( aElementName, xDest, rNewName );
+}
+
+
+// embed::XTransactedObject
+
+
+// virtual
+void SAL_CALL Storage::commit()
+{
+ // Never commit a root storage (-> has no parent)!
+ // Would lead in writing the whole document to disk.
+
+ uno::Reference< embed::XStorage > xParentStorage = getParentStorage();
+ if ( !xParentStorage.is() )
+ return;
+
+ OSL_ENSURE( m_xWrappedTransObj.is(), "No XTransactedObject interface!" );
+
+ if ( !m_xWrappedTransObj.is() )
+ return;
+
+ m_xWrappedTransObj->commit();
+
+ if ( !isParentARootStorage() )
+ {
+ uno::Reference< embed::XTransactedObject > xParentTA(
+ xParentStorage, uno::UNO_QUERY );
+ OSL_ENSURE( xParentTA.is(), "No XTransactedObject interface!" );
+
+ if ( xParentTA.is() )
+ xParentTA->commit();
+ }
+}
+
+
+// virtual
+void SAL_CALL Storage::revert()
+{
+ uno::Reference< embed::XStorage > xParentStorage = getParentStorage();
+ if ( !xParentStorage.is() )
+ return;
+
+ OSL_ENSURE( m_xWrappedTransObj.is(), "No XTransactedObject interface!" );
+
+ if ( !m_xWrappedTransObj.is() )
+ return;
+
+ m_xWrappedTransObj->revert();
+
+ if ( !isParentARootStorage() )
+ {
+ uno::Reference< embed::XTransactedObject > xParentTA(
+ xParentStorage, uno::UNO_QUERY );
+ OSL_ENSURE( xParentTA.is(), "No XTransactedObject interface!" );
+
+ if ( xParentTA.is() )
+ xParentTA->revert();
+ }
+}
+
+
+// OutputStream Implementation.
+
+
+OutputStream::OutputStream(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ const OUString & rUri,
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const uno::Reference< io::XOutputStream > & xStreamToWrap )
+: ParentStorageHolder( xParentStorage, Uri( rUri ).getParentUri() ),
+ m_xWrappedStream( xStreamToWrap ),
+ m_xWrappedComponent( xStreamToWrap, uno::UNO_QUERY ),
+ m_xWrappedTypeProv( xStreamToWrap, uno::UNO_QUERY )
+{
+ OSL_ENSURE( m_xWrappedStream.is(),
+ "OutputStream::OutputStream: No stream to wrap!" );
+
+ OSL_ENSURE( m_xWrappedComponent.is(),
+ "OutputStream::OutputStream: No component to wrap!" );
+
+ OSL_ENSURE( m_xWrappedTypeProv.is(),
+ "OutputStream::OutputStream: No Type Provider!" );
+
+ // Use proxy factory service to create aggregatable proxy.
+ try
+ {
+ uno::Reference< reflection::XProxyFactory > xProxyFac =
+ reflection::ProxyFactory::create( rxContext );
+ m_xAggProxy = xProxyFac->createProxy( m_xWrappedStream );
+ }
+ catch ( uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ OSL_ENSURE( m_xAggProxy.is(),
+ "OutputStream::OutputStream: Wrapped stream cannot be aggregated!" );
+
+ if ( !m_xAggProxy.is() )
+ return;
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // Solaris compiler problem:
+ // Extra block to enforce destruction of temporary object created
+ // in next statement _before_ osl_atomic_decrement is
+ // called. Otherwise 'this' will destroy itself even before ctor
+ // is completed (See impl. of XInterface::release())!
+
+ m_xAggProxy->setDelegator(
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+// virtual
+OutputStream::~OutputStream()
+{
+ if ( m_xAggProxy.is() )
+ m_xAggProxy->setDelegator( uno::Reference< uno::XInterface >() );
+}
+
+
+// uno::XInterface
+
+
+// virtual
+uno::Any SAL_CALL OutputStream::queryInterface( const uno::Type& aType )
+{
+ uno::Any aRet = OutputStreamUNOBase::queryInterface( aType );
+
+ if ( aRet.hasValue() )
+ return aRet;
+
+ if ( m_xAggProxy.is() )
+ return m_xAggProxy->queryAggregation( aType );
+ else
+ return uno::Any();
+}
+
+
+// lang::XTypeProvider
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL OutputStream::getTypes()
+{
+ return m_xWrappedTypeProv->getTypes();
+}
+
+
+// virtual
+uno::Sequence< sal_Int8 > SAL_CALL OutputStream::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+// io::XOutputStream
+
+
+// virtual
+void SAL_CALL
+OutputStream::writeBytes( const uno::Sequence< sal_Int8 >& aData )
+{
+ m_xWrappedStream->writeBytes( aData );
+}
+
+
+// virtual
+void SAL_CALL
+OutputStream::flush()
+{
+ m_xWrappedStream->flush();
+}
+
+
+// virtual
+void SAL_CALL
+OutputStream::closeOutput( )
+{
+ m_xWrappedStream->closeOutput();
+
+ // Release parent storage.
+ // Now, that the stream is closed/disposed it is not needed any longer.
+ setParentStorage( uno::Reference< embed::XStorage >() );
+}
+
+
+// lang::XComponent
+
+
+// virtual
+void SAL_CALL
+OutputStream::dispose()
+{
+ m_xWrappedComponent->dispose();
+
+ // Release parent storage.
+ // Now, that the stream is closed/disposed it is not needed any longer.
+ setParentStorage( uno::Reference< embed::XStorage >() );
+}
+
+
+// virtual
+void SAL_CALL
+OutputStream::addEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ m_xWrappedComponent->addEventListener( xListener );
+}
+
+
+// virtual
+void SAL_CALL
+OutputStream::removeEventListener(
+ const uno::Reference< lang::XEventListener >& aListener )
+{
+ m_xWrappedComponent->removeEventListener( aListener );
+}
+
+
+// Stream Implementation.
+
+
+Stream::Stream(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ const OUString & rUri,
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const uno::Reference< io::XStream > & xStreamToWrap )
+: ParentStorageHolder( xParentStorage, Uri( rUri ).getParentUri() ),
+ m_xWrappedStream( xStreamToWrap ),
+ m_xWrappedOutputStream( xStreamToWrap->getOutputStream() ), // might be empty
+ m_xWrappedTruncate( m_xWrappedOutputStream, uno::UNO_QUERY ), // might be empty
+ m_xWrappedInputStream( xStreamToWrap->getInputStream() ),
+ m_xWrappedComponent( xStreamToWrap, uno::UNO_QUERY ),
+ m_xWrappedTypeProv( xStreamToWrap, uno::UNO_QUERY )
+{
+ OSL_ENSURE( m_xWrappedStream.is(),
+ "OutputStream::OutputStream: No stream to wrap!" );
+
+ OSL_ENSURE( m_xWrappedComponent.is(),
+ "OutputStream::OutputStream: No component to wrap!" );
+
+ OSL_ENSURE( m_xWrappedTypeProv.is(),
+ "OutputStream::OutputStream: No Type Provider!" );
+
+ // Use proxy factory service to create aggregatable proxy.
+ try
+ {
+ uno::Reference< reflection::XProxyFactory > xProxyFac =
+ reflection::ProxyFactory::create( rxContext );
+ m_xAggProxy = xProxyFac->createProxy( m_xWrappedStream );
+ }
+ catch ( uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("ucb.ucp", "");
+ }
+
+ OSL_ENSURE( m_xAggProxy.is(),
+ "OutputStream::OutputStream: Wrapped stream cannot be aggregated!" );
+
+ if ( !m_xAggProxy.is() )
+ return;
+
+ osl_atomic_increment( &m_refCount );
+ {
+ // Solaris compiler problem:
+ // Extra block to enforce destruction of temporary object created
+ // in next statement _before_ osl_atomic_decrement is
+ // called. Otherwise 'this' will destroy itself even before ctor
+ // is completed (See impl. of XInterface::release())!
+
+ m_xAggProxy->setDelegator(
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+
+// virtual
+Stream::~Stream()
+{
+ if ( m_xAggProxy.is() )
+ m_xAggProxy->setDelegator( uno::Reference< uno::XInterface >() );
+}
+
+
+// uno::XInterface
+
+
+// virtual
+uno::Any SAL_CALL Stream::queryInterface( const uno::Type& aType )
+{
+ uno::Any aRet = StreamUNOBase::queryInterface( aType );
+
+ if ( aRet.hasValue() )
+ return aRet;
+
+ if ( m_xAggProxy.is() )
+ return m_xAggProxy->queryAggregation( aType );
+ else
+ return uno::Any();
+}
+
+
+// lang::XTypeProvider
+
+
+// virtual
+uno::Sequence< uno::Type > SAL_CALL Stream::getTypes()
+{
+ return m_xWrappedTypeProv->getTypes();
+}
+
+
+// virtual
+uno::Sequence< sal_Int8 > SAL_CALL Stream::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+// io::XStream.
+
+
+// virtual
+uno::Reference< io::XInputStream > SAL_CALL Stream::getInputStream()
+{
+ return uno::Reference< io::XInputStream >( this );
+}
+
+
+// virtual
+uno::Reference< io::XOutputStream > SAL_CALL Stream::getOutputStream()
+{
+ return uno::Reference< io::XOutputStream >( this );
+}
+
+
+// io::XOutputStream.
+
+
+// virtual
+void SAL_CALL Stream::writeBytes( const uno::Sequence< sal_Int8 >& aData )
+{
+ if ( m_xWrappedOutputStream.is() )
+ {
+ m_xWrappedOutputStream->writeBytes( aData );
+ commitChanges();
+ }
+}
+
+
+// virtual
+void SAL_CALL Stream::flush()
+{
+ if ( m_xWrappedOutputStream.is() )
+ {
+ m_xWrappedOutputStream->flush();
+ commitChanges();
+ }
+}
+
+
+// virtual
+void SAL_CALL Stream::closeOutput()
+{
+ if ( m_xWrappedOutputStream.is() )
+ {
+ m_xWrappedOutputStream->closeOutput();
+ commitChanges();
+ }
+
+ // Release parent storage.
+ // Now, that the stream is closed/disposed it is not needed any longer.
+ setParentStorage( uno::Reference< embed::XStorage >() );
+}
+
+
+// io::XTruncate.
+
+
+// virtual
+void SAL_CALL Stream::truncate()
+{
+ if ( m_xWrappedTruncate.is() )
+ {
+ m_xWrappedTruncate->truncate();
+ commitChanges();
+ }
+}
+
+
+// io::XInputStream.
+
+
+// virtual
+sal_Int32 SAL_CALL Stream::readBytes( uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead )
+{
+ return m_xWrappedInputStream->readBytes( aData, nBytesToRead );
+}
+
+
+// virtual
+sal_Int32 SAL_CALL Stream::readSomeBytes( uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nMaxBytesToRead )
+{
+ return m_xWrappedInputStream->readSomeBytes( aData, nMaxBytesToRead );
+}
+
+
+// virtual
+void SAL_CALL Stream::skipBytes( sal_Int32 nBytesToSkip )
+{
+ m_xWrappedInputStream->skipBytes( nBytesToSkip );
+}
+
+
+// virtual
+sal_Int32 SAL_CALL Stream::available()
+{
+ return m_xWrappedInputStream->available();
+}
+
+
+// virtual
+void SAL_CALL Stream::closeInput()
+{
+ m_xWrappedInputStream->closeInput();
+}
+
+
+// lang::XComponent
+
+
+// virtual
+void SAL_CALL Stream::dispose()
+{
+ m_xWrappedComponent->dispose();
+
+ // Release parent storage.
+ // Now, that the stream is closed/disposed it is not needed any longer.
+ setParentStorage( uno::Reference< embed::XStorage >() );
+}
+
+
+// virtual
+void SAL_CALL Stream::addEventListener(
+ const uno::Reference< lang::XEventListener >& xListener )
+{
+ m_xWrappedComponent->addEventListener( xListener );
+}
+
+
+// virtual
+void SAL_CALL Stream::removeEventListener(
+ const uno::Reference< lang::XEventListener >& aListener )
+{
+ m_xWrappedComponent->removeEventListener( aListener );
+}
+
+
+// Non-UNO
+
+
+void Stream::commitChanges()
+{
+ uno::Reference< embed::XTransactedObject >
+ xParentTA( getParentStorage(), uno::UNO_QUERY );
+ OSL_ENSURE( xParentTA.is(), "No XTransactedObject interface!" );
+
+ if ( xParentTA.is() )
+ {
+ try
+ {
+ xParentTA->commit();
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ throw io::IOException(); // @@@
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_stgelems.hxx b/ucb/source/ucp/tdoc/tdoc_stgelems.hxx
new file mode 100644
index 000000000..4842a3711
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_stgelems.hxx
@@ -0,0 +1,336 @@
+/* -*- 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/ref.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/uno/XAggregation.hpp>
+
+#include "tdoc_storage.hxx"
+
+#include <mutex>
+
+namespace tdoc_ucp {
+
+class ParentStorageHolder
+{
+public:
+ ParentStorageHolder(
+ const css::uno::Reference< css::embed::XStorage > & xParentStorage,
+ const OUString & rUri );
+
+ bool isParentARootStorage() const
+ { return m_bParentIsRootStorage; }
+ const css::uno::Reference< css::embed::XStorage >&
+ getParentStorage() const
+ { return m_xParentStorage; }
+ void setParentStorage( const css::uno::Reference< css::embed::XStorage > & xStg )
+ {
+ std::scoped_lock aGuard( m_aMutex );
+ m_xParentStorage = xStg;
+ }
+
+private:
+ std::mutex m_aMutex;
+ css::uno::Reference< css::embed::XStorage > m_xParentStorage;
+ bool m_bParentIsRootStorage;
+};
+
+
+typedef
+ cppu::WeakImplHelper<
+ css::embed::XStorage,
+ css::embed::XTransactedObject > StorageUNOBase;
+
+class Storage : public StorageUNOBase, public ParentStorageHolder
+{
+public:
+ Storage(
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const rtl::Reference< StorageElementFactory > & xFactory,
+ const OUString & rUri,
+ const css::uno::Reference< css::embed::XStorage > & xParentStorage,
+ const css::uno::Reference< css::embed::XStorage > & xStorageToWrap );
+ virtual ~Storage() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire()
+ noexcept override;
+ virtual void SAL_CALL release()
+ noexcept override;
+
+ // XTypeProvider (implemented by base, but needs to be overridden for
+ // delegating to aggregate)
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ // XComponent ( one of XStorage bases )
+ virtual void SAL_CALL
+ dispose() override;
+ virtual void SAL_CALL
+ addEventListener( const css::uno::Reference< css::lang::XEventListener > & xListener ) override;
+ virtual void SAL_CALL
+ removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XNameAccess ( one of XStorage bases )
+ 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;
+
+ // XElementAccess (base of XNameAccess)
+ virtual css::uno::Type SAL_CALL
+ getElementType() override;
+ virtual sal_Bool SAL_CALL
+ hasElements() override;
+
+ // XStorage
+ virtual void SAL_CALL
+ copyToStorage( const css::uno::Reference< css::embed::XStorage >& xDest ) override;
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL
+ openStreamElement( const OUString& aStreamName,
+ sal_Int32 nOpenMode ) override;
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL
+ openEncryptedStreamElement( const OUString& aStreamName,
+ sal_Int32 nOpenMode,
+ const OUString& aPassword ) override;
+ virtual css::uno::Reference< css::embed::XStorage > SAL_CALL
+ openStorageElement( const OUString& aStorName,
+ sal_Int32 nOpenMode ) override;
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL
+ cloneStreamElement( const OUString& aStreamName ) override;
+ virtual css::uno::Reference< css::io::XStream > SAL_CALL
+ cloneEncryptedStreamElement( const OUString& aStreamName,
+ const OUString& aPassword ) override;
+ virtual void SAL_CALL
+ copyLastCommitTo( const css::uno::Reference<
+ css::embed::XStorage >& xTargetStorage ) override;
+ virtual void SAL_CALL
+ copyStorageElementLastCommitTo( const OUString& aStorName,
+ const css::uno::Reference<
+ css::embed::XStorage > &
+ xTargetStorage ) override;
+ virtual sal_Bool SAL_CALL
+ isStreamElement( const OUString& aElementName ) override;
+ virtual sal_Bool SAL_CALL
+ isStorageElement( const OUString& aElementName ) override;
+ virtual void SAL_CALL
+ removeElement( const OUString& aElementName ) override;
+ virtual void SAL_CALL
+ renameElement( const OUString& aEleName,
+ const OUString& aNewName ) override;
+ virtual void SAL_CALL
+ copyElementTo( const OUString& aElementName,
+ const css::uno::Reference< css::embed::XStorage >& xDest,
+ const OUString& aNewName ) override;
+ virtual void SAL_CALL
+ moveElementTo( const OUString& aElementName,
+ const css::uno::Reference< css::embed::XStorage >& xDest,
+ const OUString& rNewName ) override;
+
+ // XTransactedObject
+ virtual void SAL_CALL commit() override;
+ virtual void SAL_CALL revert() override;
+
+private:
+ rtl::Reference< StorageElementFactory > m_xFactory;
+ css::uno::Reference< css::uno::XAggregation > m_xAggProxy;
+ css::uno::Reference< css::embed::XStorage > m_xWrappedStorage;
+ css::uno::Reference< css::embed::XTransactedObject > m_xWrappedTransObj;
+ css::uno::Reference< css::lang::XComponent > m_xWrappedComponent;
+ css::uno::Reference< css::lang::XTypeProvider > m_xWrappedTypeProv;
+ bool m_bIsDocumentStorage;
+
+ StorageElementFactory::StorageMap::iterator m_aContainerIt;
+
+ friend class StorageElementFactory;
+};
+
+
+typedef
+ cppu::WeakImplHelper<
+ css::io::XOutputStream,
+ css::lang::XComponent > OutputStreamUNOBase;
+
+class OutputStream : public OutputStreamUNOBase, public ParentStorageHolder
+{
+public:
+ OutputStream(
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const OUString & rUri,
+ const css::uno::Reference< css::embed::XStorage > & xParentStorage,
+ const css::uno::Reference< css::io::XOutputStream > & xStreamToWrap );
+ virtual ~OutputStream() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL
+ queryInterface( const css::uno::Type& aType ) override;
+
+ // XTypeProvider (implemented by base, but needs to be overridden for
+ // delegating to aggregate)
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ // XOutputStream
+ virtual void SAL_CALL
+ writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override;
+ virtual void SAL_CALL
+ flush( ) override;
+ // Note: We need to intercept this one.
+ virtual void SAL_CALL
+ closeOutput( ) override;
+
+ // XComponent
+ // Note: We need to intercept this one.
+ virtual void SAL_CALL
+ dispose() override;
+ virtual void SAL_CALL
+ addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL
+ removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+private:
+ css::uno::Reference<
+ css::uno::XAggregation > m_xAggProxy;
+ css::uno::Reference<
+ css::io::XOutputStream > m_xWrappedStream;
+ css::uno::Reference<
+ css::lang::XComponent > m_xWrappedComponent;
+ css::uno::Reference<
+ css::lang::XTypeProvider > m_xWrappedTypeProv;
+};
+
+
+typedef cppu::WeakImplHelper< css::io::XStream,
+ css::io::XOutputStream,
+ css::io::XTruncate,
+ css::io::XInputStream,
+ css::lang::XComponent >
+ StreamUNOBase;
+
+class Stream : public StreamUNOBase, public ParentStorageHolder
+{
+public:
+ Stream(
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const OUString & rUri,
+ const css::uno::Reference< css::embed::XStorage > & xParentStorage,
+ const css::uno::Reference< css::io::XStream > & xStreamToWrap );
+
+ virtual ~Stream() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL
+ queryInterface( const css::uno::Type& aType ) override;
+
+ // XTypeProvider (implemented by base, but needs to be overridden for
+ // delegating to aggregate)
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL
+ getTypes() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ // XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL
+ getInputStream() override;
+
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL
+ getOutputStream() override;
+
+ // XOutputStream
+ virtual void SAL_CALL
+ writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override;
+
+ virtual void SAL_CALL
+ flush() override;
+
+ virtual void SAL_CALL
+ closeOutput() override;
+
+ // XTruncate
+ virtual void SAL_CALL
+ truncate() override;
+
+ // XInputStream
+ virtual sal_Int32 SAL_CALL
+ readBytes( css::uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nBytesToRead ) override;
+
+ virtual sal_Int32 SAL_CALL
+ readSomeBytes( css::uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nMaxBytesToRead ) override;
+
+ virtual void SAL_CALL
+ skipBytes( sal_Int32 nBytesToSkip ) override;
+
+ virtual sal_Int32 SAL_CALL
+ available() override;
+
+ virtual void SAL_CALL
+ closeInput() override;
+
+ // XComponent
+ // Note: We need to intercept this one.
+ virtual void SAL_CALL
+ dispose() override;
+ virtual void SAL_CALL
+ addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL
+ removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+private:
+ /// @throws css::io::IOException
+ void commitChanges();
+
+ css::uno::Reference<
+ css::uno::XAggregation > m_xAggProxy;
+ css::uno::Reference<
+ css::io::XStream > m_xWrappedStream;
+ css::uno::Reference<
+ css::io::XOutputStream > m_xWrappedOutputStream;
+ css::uno::Reference<
+ css::io::XTruncate > m_xWrappedTruncate;
+ css::uno::Reference<
+ css::io::XInputStream > m_xWrappedInputStream;
+ css::uno::Reference<
+ css::lang::XComponent > m_xWrappedComponent;
+ css::uno::Reference<
+ css::lang::XTypeProvider > m_xWrappedTypeProv;
+};
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_storage.cxx b/ucb/source/ucp/tdoc/tdoc_storage.cxx
new file mode 100644
index 000000000..7fc0f247a
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_storage.cxx
@@ -0,0 +1,617 @@
+/* -*- 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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/InvalidStorageException.hpp>
+#include <com/sun/star/embed/StorageFactory.hpp>
+#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/packages/NoEncryptionException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+
+#include "tdoc_uri.hxx"
+#include "tdoc_docmgr.hxx"
+#include "tdoc_stgelems.hxx"
+
+#include "tdoc_storage.hxx"
+
+using namespace com::sun::star;
+using namespace tdoc_ucp;
+
+
+// StorageElementFactory Implementation.
+
+
+StorageElementFactory::StorageElementFactory(
+ const uno::Reference< uno::XComponentContext > & rxContext,
+ const rtl::Reference< OfficeDocumentsManager > & xDocsMgr )
+: m_xDocsMgr( xDocsMgr ),
+ m_xContext( rxContext )
+{
+}
+
+
+StorageElementFactory::~StorageElementFactory()
+{
+ OSL_ENSURE( m_aMap.empty(),
+ "StorageElementFactory::~StorageElementFactory - Dangling storages!" );
+}
+
+
+uno::Reference< embed::XStorage >
+StorageElementFactory::createTemporaryStorage()
+{
+ uno::Reference< embed::XStorage > xStorage;
+ uno::Reference< lang::XSingleServiceFactory > xStorageFac;
+ if ( m_xContext.is() )
+ {
+ xStorageFac = embed::StorageFactory::create( m_xContext );
+ }
+
+ OSL_ENSURE( xStorageFac.is(), "Can't create storage factory!" );
+ if ( xStorageFac.is() )
+ xStorage.set( xStorageFac->createInstance(), uno::UNO_QUERY );
+
+ if ( !xStorage.is() )
+ throw uno::RuntimeException();
+
+ return xStorage;
+}
+
+
+uno::Reference< embed::XStorage >
+StorageElementFactory::createStorage( const OUString & rUri,
+ StorageAccessMode eMode )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( ( eMode != READ ) &&
+ ( eMode != READ_WRITE_NOCREATE ) &&
+ ( eMode != READ_WRITE_CREATE ) )
+ throw lang::IllegalArgumentException(
+ "Invalid open mode!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 2 ) );
+
+ Uri aUri( rUri );
+ if ( aUri.isRoot() )
+ {
+ throw lang::IllegalArgumentException(
+ "Root never has a storage!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 1 ) );
+ }
+
+ OUString aUriKey
+ ( rUri.endsWith("/")
+ ? rUri.copy( 0, rUri.getLength() - 1 )
+ : rUri );
+
+ StorageMap::iterator aIt ( m_aMap.begin() );
+ StorageMap::iterator aEnd( m_aMap.end() );
+
+ while ( aIt != aEnd )
+ {
+ if ( (*aIt).first.first == aUriKey )
+ {
+ // URI matches. Now, check open mode.
+ bool bMatch = true;
+ switch ( eMode )
+ {
+ case READ:
+ // No need to check; storage is at least readable.
+ bMatch = true;
+ break;
+
+ case READ_WRITE_NOCREATE:
+ case READ_WRITE_CREATE:
+ // If found storage is writable, it can be used.
+ // If not, a new one must be created.
+ bMatch = (*aIt).first.second;
+ break;
+ }
+
+ if ( bMatch )
+ break;
+ }
+ ++aIt;
+ }
+
+ if ( aIt == aEnd )
+ {
+ uno::Reference< embed::XStorage > xParentStorage;
+
+ // documents never have a parent storage.
+ if ( !aUri.isDocument() )
+ {
+ xParentStorage = queryParentStorage( aUriKey, eMode );
+
+ if ( !xParentStorage.is() )
+ {
+ // requested to create new storage, but failed?
+ OSL_ENSURE( eMode != READ_WRITE_CREATE,
+ "Unable to create parent storage!" );
+ return xParentStorage;
+ }
+ }
+
+ uno::Reference< embed::XStorage > xStorage
+ = queryStorage( xParentStorage, aUriKey, eMode );
+
+ if ( !xStorage.is() )
+ {
+ // requested to create new storage, but failed?
+ OSL_ENSURE( eMode != READ_WRITE_CREATE,
+ "Unable to create storage!" );
+ return xStorage;
+ }
+
+ bool bWritable = ( ( eMode == READ_WRITE_NOCREATE )
+ || ( eMode == READ_WRITE_CREATE ) );
+
+ rtl::Reference< Storage > xElement(
+ new Storage( m_xContext, this, aUriKey, xParentStorage, xStorage ) );
+
+ aIt = m_aMap.emplace(
+ std::pair< OUString, bool >( aUriKey, bWritable ),
+ xElement.get() ).first;
+
+ aIt->second->m_aContainerIt = aIt;
+ return aIt->second;
+ }
+ else if ( osl_atomic_increment( &aIt->second->m_refCount ) > 1 )
+ {
+ uno::Reference< embed::XStorage > xElement( aIt->second );
+ osl_atomic_decrement( &aIt->second->m_refCount );
+ return xElement;
+ }
+ else
+ {
+ osl_atomic_decrement( &aIt->second->m_refCount );
+ aIt->second->m_aContainerIt = m_aMap.end();
+
+ uno::Reference< embed::XStorage > xParentStorage;
+
+ // documents never have a parent storage.
+ if ( !aUri.isDocument() )
+ {
+ xParentStorage = queryParentStorage( aUriKey, eMode );
+
+ if ( !xParentStorage.is() )
+ {
+ // requested to create new storage, but failed?
+ OSL_ENSURE( eMode != READ_WRITE_CREATE,
+ "Unable to create parent storage!" );
+ return xParentStorage;
+ }
+ }
+
+ uno::Reference< embed::XStorage > xStorage
+ = queryStorage( xParentStorage, aUriKey, eMode );
+
+ if ( !xStorage.is() )
+ {
+ // requested to create new storage, but failed?
+ OSL_ENSURE( eMode != READ_WRITE_CREATE,
+ "Unable to create storage!" );
+ return xStorage;
+ }
+
+ rtl::Reference<Storage> pNewStorage = new Storage( m_xContext, this, aUriKey, xParentStorage, xStorage );
+ aIt->second = pNewStorage.get();
+ aIt->second->m_aContainerIt = aIt;
+ return pNewStorage;
+ }
+}
+
+
+uno::Reference< io::XInputStream >
+StorageElementFactory::createInputStream( const OUString & rUri,
+ const OUString & rPassword )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ uno::Reference< embed::XStorage > xParentStorage
+ = queryParentStorage( rUri, READ );
+
+ // Each stream must have a parent storage.
+ if ( !xParentStorage.is() )
+ return uno::Reference< io::XInputStream >();
+
+ uno::Reference< io::XStream > xStream
+ = queryStream( xParentStorage, rUri, rPassword, READ, false );
+
+ if ( !xStream.is() )
+ return uno::Reference< io::XInputStream >();
+
+ return xStream->getInputStream();
+}
+
+
+uno::Reference< io::XOutputStream >
+StorageElementFactory::createOutputStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ uno::Reference< embed::XStorage > xParentStorage
+ = queryParentStorage( rUri, READ_WRITE_CREATE );
+
+ // Each stream must have a parent storage.
+ if ( !xParentStorage.is() )
+ {
+ OSL_FAIL( "StorageElementFactory::createOutputStream - "
+ "Unable to create parent storage!" );
+ return uno::Reference< io::XOutputStream >();
+ }
+
+ uno::Reference< io::XStream > xStream
+ = queryStream(
+ xParentStorage, rUri, rPassword, READ_WRITE_CREATE, bTruncate );
+
+ if ( !xStream.is() )
+ {
+ OSL_FAIL( "StorageElementFactory::createOutputStream - "
+ "Unable to create stream!" );
+ return uno::Reference< io::XOutputStream >();
+ }
+
+ // Note: We need a wrapper to hold a reference to the parent storage to
+ // ensure that nobody else owns it at the moment we want to commit
+ // our changes. (There can be only one writable instance at a time
+ // and even no writable instance if there is already another
+ // read-only instance!)
+ return uno::Reference< io::XOutputStream >(
+ new OutputStream( m_xContext, rUri, xParentStorage, xStream->getOutputStream() ) );
+}
+
+
+uno::Reference< io::XStream >
+StorageElementFactory::createStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ uno::Reference< embed::XStorage > xParentStorage
+ = queryParentStorage( rUri, READ_WRITE_CREATE );
+
+ // Each stream must have a parent storage.
+ if ( !xParentStorage.is() )
+ {
+ OSL_FAIL( "StorageElementFactory::createStream - "
+ "Unable to create parent storage!" );
+ return uno::Reference< io::XStream >();
+ }
+
+ uno::Reference< io::XStream > xStream
+ = queryStream(
+ xParentStorage, rUri, rPassword, READ_WRITE_NOCREATE, bTruncate );
+
+ if ( !xStream.is() )
+ {
+ OSL_FAIL( "StorageElementFactory::createStream - "
+ "Unable to create stream!" );
+ return uno::Reference< io::XStream >();
+ }
+
+ return uno::Reference< io::XStream >(
+ new Stream( m_xContext, rUri, xParentStorage, xStream ) );
+}
+
+
+void StorageElementFactory::releaseElement( Storage const * pElement )
+{
+ OSL_ASSERT( pElement );
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( pElement->m_aContainerIt != m_aMap.end() )
+ m_aMap.erase( pElement->m_aContainerIt );
+}
+
+
+// Non-UNO interface
+
+
+uno::Reference< embed::XStorage > StorageElementFactory::queryParentStorage(
+ const OUString & rUri, StorageAccessMode eMode )
+{
+ uno::Reference< embed::XStorage > xParentStorage;
+
+ Uri aUri( rUri );
+ Uri aParentUri( aUri.getParentUri() );
+ if ( !aParentUri.isRoot() )
+ {
+ xParentStorage = createStorage( aUri.getParentUri(), eMode );
+ OSL_ENSURE( xParentStorage.is()
+ // requested to create new storage, but failed?
+ || ( eMode != READ_WRITE_CREATE ),
+ "StorageElementFactory::queryParentStorage - No storage!" );
+ }
+ return xParentStorage;
+}
+
+
+uno::Reference< embed::XStorage > StorageElementFactory::queryStorage(
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const OUString & rUri,
+ StorageAccessMode eMode )
+{
+ uno::Reference< embed::XStorage > xStorage;
+
+ Uri aUri( rUri );
+
+ if ( !xParentStorage.is() )
+ {
+ // document storage
+
+ xStorage = m_xDocsMgr->queryStorage( aUri.getDocumentId() );
+
+ if ( !xStorage.is() )
+ {
+ if ( eMode == READ_WRITE_CREATE )
+ throw lang::IllegalArgumentException(
+ "Invalid open mode: document storages cannot be created!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 2 ) );
+ else
+ throw embed::InvalidStorageException(
+ "Invalid document id!",
+ uno::Reference< uno::XInterface >() );
+ }
+
+ // match xStorage's open mode against requested open mode
+
+ uno::Reference< beans::XPropertySet > xPropSet(
+ xStorage, uno::UNO_QUERY );
+ OSL_ENSURE( xPropSet.is(),
+ "StorageElementFactory::queryStorage - "
+ "No XPropertySet interface!" );
+ try
+ {
+ uno::Any aPropValue = xPropSet->getPropertyValue("OpenMode");
+
+ sal_Int32 nOpenMode = 0;
+ if ( aPropValue >>= nOpenMode )
+ {
+ switch ( eMode )
+ {
+ case READ:
+ if ( !( nOpenMode & embed::ElementModes::READ ) )
+ {
+ // document opened, but not readable.
+ throw embed::InvalidStorageException(
+ "Storage is open, but not readable!" );
+ }
+ // storage okay
+ break;
+
+ case READ_WRITE_NOCREATE:
+ case READ_WRITE_CREATE:
+ if ( !( nOpenMode & embed::ElementModes::WRITE ) )
+ {
+ // document opened, but not writable.
+ throw embed::InvalidStorageException(
+ "Storage is open, but not writable!" );
+ }
+ // storage okay
+ break;
+ }
+ }
+ else
+ {
+ OSL_FAIL(
+ "Bug! Value of property OpenMode has wrong type!" );
+
+ throw uno::RuntimeException(
+ "Bug! Value of property OpenMode has wrong type!" );
+ }
+ }
+ catch ( beans::UnknownPropertyException const & )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ OSL_FAIL( "Property OpenMode not supported!" );
+
+ throw embed::StorageWrappedTargetException(
+ "Bug! Value of property OpenMode has wrong type!",
+ uno::Reference< uno::XInterface >(),
+ anyEx );
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ OSL_FAIL( "Caught WrappedTargetException!" );
+
+ throw embed::StorageWrappedTargetException(
+ "WrappedTargetException during getPropertyValue!",
+ uno::Reference< uno::XInterface >(),
+ anyEx );
+ }
+ }
+ else
+ {
+ // sub storage
+
+ const OUString & rName = aUri.getDecodedName();
+
+ if ( eMode == READ )
+ {
+ try
+ {
+ sal_Int32 const nOpenMode = embed::ElementModes::READ
+ | embed::ElementModes::NOCREATE;
+ xStorage
+ = xParentStorage->openStorageElement( rName, nOpenMode );
+ }
+ catch ( io::IOException const & )
+ {
+ // Another chance: Try to clone storage.
+ xStorage = createTemporaryStorage();
+ xParentStorage->copyStorageElementLastCommitTo( rName,
+ xStorage );
+ }
+ }
+ else
+ {
+ sal_Int32 nOpenMode = embed::ElementModes::READWRITE;
+ if ( eMode == READ_WRITE_NOCREATE )
+ nOpenMode |= embed::ElementModes::NOCREATE;
+
+ xStorage = xParentStorage->openStorageElement( rName, nOpenMode );
+ }
+ }
+
+ OSL_ENSURE( xStorage.is() || ( eMode != READ_WRITE_CREATE ),
+ "StorageElementFactory::queryStorage - No storage!" );
+ return xStorage;
+}
+
+
+uno::Reference< io::XStream >
+StorageElementFactory::queryStream(
+ const uno::Reference< embed::XStorage > & xParentStorage,
+ const OUString & rUri,
+ const OUString & rPassword,
+ StorageAccessMode eMode,
+ bool bTruncate )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !xParentStorage.is() )
+ {
+ throw lang::IllegalArgumentException(
+ "No parent storage!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 2 ) );
+ }
+
+ Uri aUri( rUri );
+ if ( aUri.isRoot() )
+ {
+ throw lang::IllegalArgumentException(
+ "Root never is a stream!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 2 ) );
+ }
+ else if ( aUri.isDocument() )
+ {
+ throw lang::IllegalArgumentException(
+ "A document never is a stream!",
+ uno::Reference< uno::XInterface >(),
+ sal_Int16( 2 ) );
+ }
+
+ sal_Int32 nOpenMode;
+ switch ( eMode )
+ {
+ case READ:
+ nOpenMode = embed::ElementModes::READ
+ | embed::ElementModes::NOCREATE
+ | embed::ElementModes::SEEKABLE;
+ break;
+
+ case READ_WRITE_NOCREATE:
+ nOpenMode = embed::ElementModes::READWRITE
+ | embed::ElementModes::NOCREATE
+ | embed::ElementModes::SEEKABLE;
+
+ if ( bTruncate )
+ nOpenMode |= embed::ElementModes::TRUNCATE;
+
+ break;
+
+ case READ_WRITE_CREATE:
+ nOpenMode = embed::ElementModes::READWRITE
+ | embed::ElementModes::SEEKABLE;
+
+ if ( bTruncate )
+ nOpenMode |= embed::ElementModes::TRUNCATE;
+
+ break;
+
+ default:
+ OSL_FAIL( "StorageElementFactory::queryStream : Unknown open mode!" );
+
+ throw embed::InvalidStorageException(
+ "Unknown open mode!",
+ uno::Reference< uno::XInterface >() );
+ }
+
+ // No object re-usage mechanism; streams are seekable => not stateless.
+
+ uno::Reference< io::XStream > xStream;
+ if ( !rPassword.isEmpty() )
+ {
+ if ( eMode == READ )
+ {
+ try
+ {
+ xStream = xParentStorage->cloneEncryptedStreamElement(
+ aUri.getDecodedName(),
+ rPassword );
+ }
+ catch ( packages::NoEncryptionException const & )
+ {
+ xStream
+ = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
+ }
+ }
+ else
+ {
+ try
+ {
+ xStream = xParentStorage->openEncryptedStreamElement(
+ aUri.getDecodedName(),
+ nOpenMode,
+ rPassword );
+ }
+ catch ( packages::NoEncryptionException const & )
+ {
+ xStream
+ = xParentStorage->openStreamElement( aUri.getDecodedName(),
+ nOpenMode );
+ }
+ }
+ }
+ else
+ {
+ if ( eMode == READ )
+ {
+ xStream = xParentStorage->cloneStreamElement( aUri.getDecodedName() );
+ }
+ else
+ {
+ xStream = xParentStorage->openStreamElement( aUri.getDecodedName(),
+ nOpenMode );
+ }
+ }
+
+ if ( !xStream.is() )
+ {
+ throw embed::InvalidStorageException(
+ "No stream!",
+ uno::Reference< uno::XInterface >() );
+ }
+
+ return xStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_storage.hxx b/ucb/source/ucp/tdoc/tdoc_storage.hxx
new file mode 100644
index 000000000..c7067aca7
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_storage.hxx
@@ -0,0 +1,162 @@
+/* -*- 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 <map>
+
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace tdoc_ucp {
+
+ enum StorageAccessMode
+ {
+ READ, // Note: might be writable as well
+ READ_WRITE_NOCREATE,
+ READ_WRITE_CREATE
+ };
+
+ class Storage;
+ class OfficeDocumentsManager;
+
+ class StorageElementFactory : public salhelper::SimpleReferenceObject
+ {
+ public:
+ StorageElementFactory(
+ const css::uno::Reference< css::uno::XComponentContext > & rxContext,
+ const rtl::Reference< OfficeDocumentsManager > & xDocsMgr );
+ virtual ~StorageElementFactory() override;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::embed::XStorage >
+ createTemporaryStorage();
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::embed::XStorage >
+ createStorage( const OUString & rUri, StorageAccessMode eMode );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XInputStream >
+ createInputStream( const OUString & rUri,
+ const OUString & rPassword );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XOutputStream >
+ createOutputStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XStream >
+ createStream( const OUString & rUri,
+ const OUString & rPassword,
+ bool bTruncate );
+
+ private:
+ friend class Storage;
+
+ void releaseElement( Storage const * pElement );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::embed::XStorage >
+ queryParentStorage( const OUString & rUri,
+ StorageAccessMode eMode );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::embed::XStorage >
+ queryStorage( const css::uno::Reference<
+ css::embed::XStorage > & xParentStorage,
+ const OUString & rUri,
+ StorageAccessMode eMode );
+
+ /// @throws css::embed::InvalidStorageException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::io::IOException
+ /// @throws css::embed::StorageWrappedTargetException
+ /// @throws css::packages::WrongPasswordException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::io::XStream >
+ queryStream( const css::uno::Reference<
+ css::embed::XStorage > & xParentStorage,
+ const OUString & rUri,
+ const OUString & rPassword,
+ StorageAccessMode eMode,
+ bool bTruncate /* ignored for read-only streams */ );
+
+ struct ltstrbool
+ {
+ bool operator()(
+ const std::pair< OUString, bool > & s1,
+ const std::pair< OUString, bool > & s2 ) const
+ {
+ if ( s1.first < s2.first )
+ return true;
+ else if ( s1.first == s2.first )
+ return ( !s1.second && s2.second );
+ else
+ return false;
+ }
+ };
+
+ // key: pair< storageuri, iswritable >
+ typedef std::map<
+ std::pair< OUString, bool >, Storage *, ltstrbool > StorageMap;
+
+ StorageMap m_aMap;
+ osl::Mutex m_aMutex;
+ rtl::Reference< OfficeDocumentsManager > m_xDocsMgr;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ };
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_uri.cxx b/ucb/source/ucp/tdoc/tdoc_uri.cxx
new file mode 100644
index 000000000..592977ea0
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_uri.cxx
@@ -0,0 +1,111 @@
+/* -*- 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 "../inc/urihelper.hxx"
+
+#include "tdoc_uri.hxx"
+
+using namespace tdoc_ucp;
+
+
+// Uri Implementation.
+
+
+void Uri::init() const
+{
+ // Already inited?
+ if ( m_eState != UNKNOWN )
+ return;
+
+ m_eState = INVALID;
+
+ // Check for proper length: must be at least length of <scheme>:/
+ if ( m_aUri.getLength() < TDOC_URL_SCHEME_LENGTH + 2 )
+ {
+ // Invalid length (to short).
+ return;
+ }
+
+ // Check for proper scheme. (Scheme is case insensitive.)
+ OUString aScheme
+ = m_aUri.copy( 0, TDOC_URL_SCHEME_LENGTH ).toAsciiLowerCase();
+ if ( aScheme != TDOC_URL_SCHEME )
+ {
+ // Invalid scheme.
+ return;
+ }
+
+ // Remember normalized scheme string.
+ m_aUri = m_aUri.replaceAt( 0, aScheme.getLength(), aScheme );
+
+ if ( m_aUri[ TDOC_URL_SCHEME_LENGTH ] != ':' )
+ {
+ // Invalid (no ':' after <scheme>).
+ return;
+ }
+
+ if ( m_aUri[ TDOC_URL_SCHEME_LENGTH + 1 ] != '/' )
+ {
+ // Invalid (no '/' after <scheme>:).
+ return;
+ }
+
+ m_aPath = m_aUri.copy( TDOC_URL_SCHEME_LENGTH + 1 );
+
+ // Note: There must be at least one slash; see above.
+ sal_Int32 nLastSlash = m_aUri.lastIndexOf( '/' );
+ bool bTrailingSlash = false;
+ if ( nLastSlash == m_aUri.getLength() - 1 )
+ {
+ // ignore trailing slash
+ bTrailingSlash = true;
+ nLastSlash = m_aUri.lastIndexOf( '/', nLastSlash );
+ }
+
+ if ( nLastSlash != -1 ) // -1 is valid for the root folder
+ {
+ m_aParentUri = m_aUri.copy( 0, nLastSlash + 1 );
+
+ if ( bTrailingSlash )
+ m_aName = m_aUri.copy( nLastSlash + 1,
+ m_aUri.getLength() - nLastSlash - 2 );
+ else
+ m_aName = m_aUri.copy( nLastSlash + 1 );
+
+ m_aDecodedName = ::ucb_impl::urihelper::decodeSegment( m_aName );
+
+ sal_Int32 nSlash = m_aPath.indexOf( '/', 1 );
+ if ( nSlash == -1 )
+ m_aDocId = m_aPath.copy( 1 );
+ else
+ m_aDocId = m_aPath.copy( 1, nSlash - 1 );
+ }
+
+ m_eState = VALID;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/tdoc_uri.hxx b/ucb/source/ucp/tdoc/tdoc_uri.hxx
new file mode 100644
index 000000000..8a459c0ae
--- /dev/null
+++ b/ucb/source/ucp/tdoc/tdoc_uri.hxx
@@ -0,0 +1,107 @@
+/* -*- 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>
+
+namespace tdoc_ucp {
+
+
+#define TDOC_URL_SCHEME "vnd.sun.star.tdoc"
+#define TDOC_URL_SCHEME_LENGTH 17
+
+
+class Uri
+{
+ enum State { UNKNOWN, INVALID, VALID };
+
+ mutable OUString m_aUri;
+ mutable OUString m_aParentUri;
+ mutable OUString m_aPath;
+ mutable OUString m_aDocId;
+ mutable OUString m_aName;
+ mutable OUString m_aDecodedName;
+ mutable State m_eState;
+
+private:
+ void init() const;
+
+public:
+ explicit Uri( const OUString & rUri )
+ : m_aUri( rUri ), m_eState( UNKNOWN ) {}
+
+ bool operator== ( const Uri & rOther ) const
+ { init(); return m_aUri == rOther.m_aUri; }
+
+ bool operator!= ( const Uri & rOther ) const
+ { return !operator==( rOther ); }
+
+ bool isValid() const
+ { init(); return m_eState == VALID; }
+
+ const OUString & getUri() const
+ { init(); return m_aUri; }
+
+ inline void setUri( const OUString & rUri );
+
+ const OUString & getParentUri() const
+ { init(); return m_aParentUri; }
+
+ const OUString & getDocumentId() const
+ { init(); return m_aDocId; }
+
+ const OUString & getName() const
+ { init(); return m_aName; }
+
+ const OUString & getDecodedName() const
+ { init(); return m_aDecodedName; }
+
+ inline bool isRoot() const;
+
+ inline bool isDocument() const;
+};
+
+inline void Uri::setUri( const OUString & rUri )
+{
+ m_eState = UNKNOWN;
+ m_aUri = rUri;
+ m_aParentUri.clear();
+ m_aDocId.clear();
+ m_aPath.clear();
+ m_aName.clear();
+ m_aDecodedName.clear();
+}
+
+inline bool Uri::isRoot() const
+{
+ init();
+ return ( m_aPath.getLength() == 1 );
+}
+
+inline bool Uri::isDocument() const
+{
+ init();
+ return ( ( !m_aDocId.isEmpty() ) /* not root */
+ && ( m_aPath.subView( m_aDocId.getLength() + 1 ).size() < 2 ) );
+}
+
+} // namespace tdoc_ucp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/tdoc/ucptdoc1.component b/ucb/source/ucp/tdoc/ucptdoc1.component
new file mode 100644
index 000000000..0ba43e266
--- /dev/null
+++ b/ucb/source/ucp/tdoc/ucptdoc1.component
@@ -0,0 +1,30 @@
+<?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.TransientDocumentsContentProvider"
+ constructor="ucb_tdoc_ContentProvider_get_implementation">
+ <service name="com.sun.star.ucb.TransientDocumentsContentProvider"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.ucb.TransientDocumentsDocumentContentFactory"
+ constructor="ucb_tdoc_DocumentContentFactory_get_implementation" single-instance="true">
+ <service name="com.sun.star.frame.TransientDocumentsDocumentContentFactory"/>
+ </implementation>
+</component>