    /* -*- 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/PropertyValue.hpp>
    #include <com/sun/star/embed/ElementModes.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/packages/WrongPasswordException.hpp>
    #include <com/sun/star/ucb/NameClash.hpp>
    #include <com/sun/star/ucb/XProgressHandler.hpp>
    #include <com/sun/star/ucb/XContentAccess.hpp>
    #include <com/sun/star/ucb/SimpleFileAccess.hpp>

    #include <com/sun/star/ucb/InteractiveIOException.hpp>
    #include <com/sun/star/ucb/IOErrorCode.hpp>
    #include <com/sun/star/container/ElementExistException.hpp>
    #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
    #include <com/sun/star/container/XEnumerationAccess.hpp>
    #include <com/sun/star/container/XNamed.hpp>
    #include <com/sun/star/util/XChangesBatch.hpp>
    #include <com/sun/star/util/XCloneable.hpp>
    #include <com/sun/star/lang/XUnoTunnel.hpp>
    #include <com/sun/star/lang/XComponent.hpp>
    #include <com/sun/star/lang/DisposedException.hpp>
    #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
    #include <com/sun/star/io/IOException.hpp>
    #include <com/sun/star/io/XTruncate.hpp>
    #include <com/sun/star/io/TempFile.hpp>
    #include <com/sun/star/sdbc/XResultSet.hpp>
    #include <com/sun/star/sdbc/XRow.hpp>

    #include <comphelper/fileurl.hxx>
    #include <comphelper/processfactory.hxx>
    #include <comphelper/storagehelper.hxx>
    #include <cppuhelper/queryinterface.hxx>
    #include <cppuhelper/typeprovider.hxx>
    #include <cppuhelper/exc_hlp.hxx>

    #include <tools/urlobj.hxx>
    #include <unotools/ucbhelper.hxx>
    #include <unotools/ucbstreamhelper.hxx>
    #include <unotools/streamwrap.hxx>
    #include <ucbhelper/content.hxx>

    #include "fsstorage.hxx"
    #include "oinputstreamcontainer.hxx"
    #include "ostreamcontainer.hxx"

    using namespace ::com::sun::star;

    struct FSStorage_Impl
    {
        OUString m_aURL;

        ::ucbhelper::Content m_aContent;
        sal_Int32 m_nMode;

        std::unique_ptr<::comphelper::OInterfaceContainerHelper2> m_pListenersContainer; // list of listeners
        std::unique_ptr<::cppu::OTypeCollection> m_pTypeCollection;

        uno::Reference< uno::XComponentContext > m_xContext;

        FSStorage_Impl( const ::ucbhelper::Content& aContent, sal_Int32 nMode, uno::Reference< uno::XComponentContext > const & xContext )
        : m_aURL( aContent.getURL() )
        , m_aContent( aContent )
        , m_nMode( nMode )
        , m_xContext( xContext )
        {
            OSL_ENSURE( !m_aURL.isEmpty(), "The URL must not be empty" );
        }

        // Copy assignment is forbidden and not implemented.
        FSStorage_Impl (const FSStorage_Impl &) = delete;
        FSStorage_Impl & operator= (const FSStorage_Impl &) = delete;
    };

    FSStorage::FSStorage( const ::ucbhelper::Content& aContent,
                        sal_Int32 nMode,
                        uno::Reference< uno::XComponentContext > const & xContext )
    : m_pImpl( new FSStorage_Impl( aContent, nMode, xContext ) )
    {
        // TODO: use properties
        if ( !xContext.is() )
            throw uno::RuntimeException();

        GetContent();
    }

    FSStorage::~FSStorage()
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        m_refCount++; // to call dispose
        try {
            dispose();
        }
        catch( uno::RuntimeException& )
        {}
    }

    bool FSStorage::MakeFolderNoUI( const OUString& rFolder )
    {
        INetURLObject aURL( rFolder );
        OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
        aURL.removeSegment();
        ::ucbhelper::Content aParent;
        ::ucbhelper::Content aResultContent;

           if ( ::ucbhelper::Content::create( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                                     uno::Reference< ucb::XCommandEnvironment >(),
                                     comphelper::getProcessComponentContext(),
                                     aParent ) )
            return ::utl::UCBContentHelper::MakeFolder( aParent, aTitle, aResultContent );

        return false;
    }

    ::ucbhelper::Content* FSStorage::GetContent()
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        return &m_pImpl->m_aContent;
    }

    void FSStorage::CopyStreamToSubStream( const OUString& aSourceURL,
                                            const uno::Reference< embed::XStorage >& xDest,
                                            const OUString& aNewEntryName )
    {
        if ( !xDest.is() )
            throw uno::RuntimeException();

        uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
        ::ucbhelper::Content aSourceContent( aSourceURL, xDummyEnv, comphelper::getProcessComponentContext() );
        uno::Reference< io::XInputStream > xSourceInput = aSourceContent.openStream();

        if ( !xSourceInput.is() )
            throw io::IOException(); // TODO: error handling

        uno::Reference< io::XStream > xSubStream = xDest->openStreamElement(
                                                    aNewEntryName,
                                                    embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
        if ( !xSubStream.is() )
            throw uno::RuntimeException();

        uno::Reference< io::XOutputStream > xDestOutput = xSubStream->getOutputStream();
        if ( !xDestOutput.is() )
            throw uno::RuntimeException();

        ::comphelper::OStorageHelper::CopyInputToOutput( xSourceInput, xDestOutput );
        xDestOutput->closeOutput();
    }

    void FSStorage::CopyContentToStorage_Impl( ::ucbhelper::Content* pContent, const uno::Reference< embed::XStorage >& xDest )
    {
        if ( !pContent )
            throw uno::RuntimeException();

        // get list of contents of the Content
        // create cursor for access to children
        uno::Sequence< OUString > aProps( 2 );
        OUString* pProps = aProps.getArray();
        pProps[0] = "TargetURL";
        pProps[1] = "IsFolder";

        try
        {
            uno::Reference< sdbc::XResultSet > xResultSet = pContent->createCursor( aProps, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS );
            uno::Reference< sdbc::XRow > xRow( xResultSet, uno::UNO_QUERY );
            if ( xResultSet.is() )
            {
                // go through the list: insert files as streams, insert folders as substorages using recursion
                while ( xResultSet->next() )
                {
                    OUString aSourceURL( xRow->getString( 1 ) );
                    bool bIsFolder( xRow->getBoolean(2) );

                    // TODO/LATER: not sure whether the entry name must be encoded
                    OUString aNewEntryName( INetURLObject( aSourceURL ).getName( INetURLObject::LAST_SEGMENT,
                                                                                        true,
                                                                                        INetURLObject::DecodeMechanism::NONE ) );
                    if ( bIsFolder )
                    {
                        uno::Reference< embed::XStorage > xSubStorage = xDest->openStorageElement( aNewEntryName,
                                                                                                    embed::ElementModes::READWRITE );
                        if ( !xSubStorage.is() )
                            throw uno::RuntimeException();

                        uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
                        ::ucbhelper::Content aSourceContent( aSourceURL, xDummyEnv, comphelper::getProcessComponentContext() );
                        CopyContentToStorage_Impl( &aSourceContent, xSubStorage );
                    }
                    else
                    {
                        CopyStreamToSubStream( aSourceURL, xDest, aNewEntryName );
                    }
                }
            }

            uno::Reference< embed::XTransactedObject > xTransact( xDest, uno::UNO_QUERY );
            if ( xTransact.is() )
                xTransact->commit();
        }
        catch( ucb::InteractiveIOException& r )
        {
            if ( r.Code == ucb::IOErrorCode_NOT_EXISTING )
                OSL_FAIL( "The folder does not exist!" );
            else
                throw;
        }
    }

    //  XInterface

    uno::Any SAL_CALL FSStorage::queryInterface( const uno::Type& rType )
    {
        uno::Any aReturn;
        aReturn = ::cppu::queryInterface
                    (   rType
                    ,   static_cast<lang::XTypeProvider*> ( this )
                    ,   static_cast<embed::XStorage*> ( this )
                    ,   static_cast<embed::XHierarchicalStorageAccess*> ( this )
                    ,   static_cast<container::XNameAccess*> ( this )
                    ,   static_cast<container::XElementAccess*> ( this )
                    ,   static_cast<lang::XComponent*> ( this )
                    ,   static_cast<beans::XPropertySet*> ( this ) );

        if ( aReturn.hasValue() )
            return aReturn ;

        return OWeakObject::queryInterface( rType );
    }

    void SAL_CALL FSStorage::acquire() throw()
    {
        OWeakObject::acquire();
    }

    void SAL_CALL FSStorage::release() throw()
    {
        OWeakObject::release();
    }

    //  XTypeProvider

    uno::Sequence< uno::Type > SAL_CALL FSStorage::getTypes()
    {
        if ( m_pImpl->m_pTypeCollection == nullptr )
        {
            ::osl::MutexGuard aGuard( m_aMutex );

            if ( m_pImpl->m_pTypeCollection == nullptr )
            {
                m_pImpl->m_pTypeCollection.reset(new ::cppu::OTypeCollection
                                    (   cppu::UnoType<lang::XTypeProvider>::get()
                                    ,   cppu::UnoType<embed::XStorage>::get()
                                    ,   cppu::UnoType<embed::XHierarchicalStorageAccess>::get()
                                    ,   cppu::UnoType<beans::XPropertySet>::get()) );
            }
        }

        return m_pImpl->m_pTypeCollection->getTypes() ;
    }

    uno::Sequence< sal_Int8 > SAL_CALL FSStorage::getImplementationId()
    {
        return css::uno::Sequence<sal_Int8>();
    }

    //  XStorage

    void SAL_CALL FSStorage::copyToStorage( const uno::Reference< embed::XStorage >& xDest )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !xDest.is() || xDest == uno::Reference< uno::XInterface >( static_cast< OWeakObject*> ( this ), uno::UNO_QUERY ) )
            throw lang::IllegalArgumentException(); // TODO:

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        try
        {
            CopyContentToStorage_Impl( GetContent(), xDest );
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }
    }

    uno::Reference< io::XStream > SAL_CALL FSStorage::openStreamElement(
        const OUString& aStreamName, sal_Int32 nOpenMode )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        // TODO/LATER: may need possibility to create folder if it was removed, since the folder can not be locked
        INetURLObject aFileURL( m_pImpl->m_aURL );
        aFileURL.Append( aStreamName );

        if ( ::utl::UCBContentHelper::IsFolder( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw io::IOException();

        if ( ( nOpenMode & embed::ElementModes::NOCREATE )
          && !::utl::UCBContentHelper::IsDocument( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw io::IOException(); // TODO:

        uno::Reference< ucb::XCommandEnvironment > xDummyEnv; // TODO: provide InteractionHandler if any
        uno::Reference< io::XStream > xResult;
        try
        {
            if ( nOpenMode & embed::ElementModes::WRITE )
            {
                if ( aFileURL.GetProtocol() == INetProtocol::File )
                {
                    uno::Reference<ucb::XSimpleFileAccess3> xSimpleFileAccess(
                        ucb::SimpleFileAccess::create( m_pImpl->m_xContext ) );
                    xResult = xSimpleFileAccess->openFileReadWrite( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
                }
                else
                {
                    // TODO: test whether it really works for http and fwp
                    std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                                                                              StreamMode::STD_WRITE );
                    if ( pStream )
                    {
                        if ( !pStream->GetError() )
                            xResult.set( new ::utl::OStreamWrapper( std::move(pStream) ) );
                    }
                }

                if ( !xResult.is() )
                    throw io::IOException();

                if ( nOpenMode & embed::ElementModes::TRUNCATE )
                {
                    uno::Reference< io::XTruncate > xTrunc( xResult->getOutputStream(), uno::UNO_QUERY_THROW );
                    xTrunc->truncate();
                }
            }
            else
            {
                if ( ( nOpenMode & embed::ElementModes::TRUNCATE )
                  || !::utl::UCBContentHelper::IsDocument( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
                    throw io::IOException(); // TODO: access denied

                ::ucbhelper::Content aResultContent( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
                uno::Reference< io::XInputStream > xInStream = aResultContent.openStream();
                xResult = static_cast< io::XStream* >( new OFSInputStreamContainer( xInStream ) );
            }
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( packages::WrongPasswordException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }

        return xResult;
    }

    uno::Reference< io::XStream > SAL_CALL FSStorage::openEncryptedStreamElement(
        const OUString&, sal_Int32, const OUString& )
    {
        throw packages::NoEncryptionException();
    }

    uno::Reference< embed::XStorage > SAL_CALL FSStorage::openStorageElement(
                const OUString& aStorName, sal_Int32 nStorageMode )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        if ( ( nStorageMode & embed::ElementModes::WRITE )
          && !( m_pImpl->m_nMode & embed::ElementModes::WRITE ) )
              throw io::IOException(); // TODO: error handling

        // TODO/LATER: may need possibility to create folder if it was removed, since the folder can not be locked
        INetURLObject aFolderURL( m_pImpl->m_aURL );
        aFolderURL.Append( aStorName );

        bool bFolderExists = ::utl::UCBContentHelper::IsFolder( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
        if ( !bFolderExists && ::utl::UCBContentHelper::IsDocument( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw io::IOException(); // TODO:

        if ( ( nStorageMode & embed::ElementModes::NOCREATE ) && !bFolderExists )
            throw io::IOException(); // TODO:

        uno::Reference< ucb::XCommandEnvironment > xDummyEnv; // TODO: provide InteractionHandler if any
        uno::Reference< embed::XStorage > xResult;
        try
        {
            if ( nStorageMode & embed::ElementModes::WRITE )
            {
                if ( ( nStorageMode & embed::ElementModes::TRUNCATE ) && bFolderExists )
                {
                    ::utl::UCBContentHelper::Kill( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
                    bFolderExists =
                        MakeFolderNoUI( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); // TODO: not atomic :(
                }
                else if ( !bFolderExists )
                {
                    bFolderExists =
                        MakeFolderNoUI( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); // TODO: not atomic :(
                }
            }
            else if ( nStorageMode & embed::ElementModes::TRUNCATE )
                throw io::IOException(); // TODO: access denied

            if ( !bFolderExists )
                throw io::IOException(); // there is no such folder

            ::ucbhelper::Content aResultContent( aFolderURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
            xResult.set( static_cast< OWeakObject* >( new FSStorage( aResultContent,
                                                                     nStorageMode,
                                                                     m_pImpl->m_xContext ) ),
                         uno::UNO_QUERY );
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }

        return xResult;
    }

    uno::Reference< io::XStream > SAL_CALL FSStorage::cloneStreamElement( const OUString& aStreamName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        // TODO/LATER: may need possibility to create folder if it was removed, since the folder can not be locked
        INetURLObject aFileURL( m_pImpl->m_aURL );
        aFileURL.Append( aStreamName );

        uno::Reference < io::XStream > xTempResult;
        try
        {
            uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
            ::ucbhelper::Content aResultContent( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
            uno::Reference< io::XInputStream > xInStream = aResultContent.openStream();

            xTempResult = io::TempFile::create(m_pImpl->m_xContext);
            uno::Reference < io::XOutputStream > xTempOut = xTempResult->getOutputStream();
            uno::Reference < io::XInputStream > xTempIn = xTempResult->getInputStream();

            if ( !xTempOut.is() || !xTempIn.is() )
                throw io::IOException();

            ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xTempOut );
            xTempOut->closeOutput();
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( packages::WrongPasswordException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }

        return xTempResult;
    }

    uno::Reference< io::XStream > SAL_CALL FSStorage::cloneEncryptedStreamElement(
        const OUString&,
        const OUString& )
    {
        throw packages::NoEncryptionException();
    }

    void SAL_CALL FSStorage::copyLastCommitTo(
                const uno::Reference< embed::XStorage >& xTargetStorage )
    {
        copyToStorage( xTargetStorage );
    }

    void SAL_CALL FSStorage::copyStorageElementLastCommitTo(
                const OUString& aStorName,
                const uno::Reference< embed::XStorage >& xTargetStorage )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        uno::Reference< embed::XStorage > xSourceStor( openStorageElement( aStorName, embed::ElementModes::READ ),
                                                        uno::UNO_QUERY_THROW );
        xSourceStor->copyToStorage( xTargetStorage );
    }

    sal_Bool SAL_CALL FSStorage::isStreamElement( const OUString& aElementName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw embed::InvalidStorageException(); // TODO: error handling

        INetURLObject aURL( m_pImpl->m_aURL );
        aURL.Append( aElementName );

        return !::utl::UCBContentHelper::IsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
    }

    sal_Bool SAL_CALL FSStorage::isStorageElement( const OUString& aElementName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw embed::InvalidStorageException(); // TODO: error handling

        INetURLObject aURL( m_pImpl->m_aURL );
        aURL.Append( aElementName );

        return ::utl::UCBContentHelper::IsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
    }

    void SAL_CALL FSStorage::removeElement( const OUString& aElementName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        INetURLObject aURL( m_pImpl->m_aURL );
        aURL.Append( aElementName );

        if ( !::utl::UCBContentHelper::IsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
          && !::utl::UCBContentHelper::IsDocument( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw container::NoSuchElementException(); // TODO:

        ::utl::UCBContentHelper::Kill( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
    }

    void SAL_CALL FSStorage::renameElement( const OUString& aElementName, const OUString& aNewName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        INetURLObject aOldURL( m_pImpl->m_aURL );
        aOldURL.Append( aElementName );

        INetURLObject aNewURL( m_pImpl->m_aURL );
        aNewURL.Append( aNewName );

        if ( !::utl::UCBContentHelper::IsFolder( aOldURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
          && !::utl::UCBContentHelper::IsDocument( aOldURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw container::NoSuchElementException(); // TODO:

        if ( ::utl::UCBContentHelper::IsFolder( aNewURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
          || ::utl::UCBContentHelper::IsDocument( aNewURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
              throw container::ElementExistException(); // TODO:

        try
        {
            uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
            ::ucbhelper::Content aSourceContent( aOldURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );

            GetContent()->transferContent( aSourceContent,
                                                ::ucbhelper::InsertOperation::Move,
                                                aNewName,
                                                ucb::NameClash::ERROR );
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( container::NoSuchElementException& )
        {
            throw;
        }
        catch( container::ElementExistException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }
    }

    void SAL_CALL FSStorage::copyElementTo( const OUString& aElementName,
                                            const uno::Reference< embed::XStorage >& xDest,
                                            const OUString& aNewName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !xDest.is() )
            throw uno::RuntimeException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        INetURLObject aOwnURL( m_pImpl->m_aURL );
        aOwnURL.Append( aElementName );

        if ( xDest->hasByName( aNewName ) )
              throw container::ElementExistException(); // TODO:

        try
        {
            uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
            if ( ::utl::UCBContentHelper::IsFolder( aOwnURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            {
                ::ucbhelper::Content aSourceContent( aOwnURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
                uno::Reference< embed::XStorage > xDestSubStor(
                                        xDest->openStorageElement( aNewName, embed::ElementModes::READWRITE ),
                                        uno::UNO_QUERY_THROW );

                CopyContentToStorage_Impl( &aSourceContent, xDestSubStor );
            }
            else if ( ::utl::UCBContentHelper::IsDocument( aOwnURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            {
                CopyStreamToSubStream( aOwnURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDest, aNewName );
            }
            else
                throw container::NoSuchElementException(); // TODO:
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( container::NoSuchElementException& )
        {
            throw;
        }
        catch( container::ElementExistException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }
    }

    void SAL_CALL FSStorage::moveElementTo( const OUString& aElementName,
                                            const uno::Reference< embed::XStorage >& xDest,
                                            const OUString& aNewName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        copyElementTo( aElementName, xDest, aNewName );

        INetURLObject aOwnURL( m_pImpl->m_aURL );
        aOwnURL.Append( aElementName );
        if ( !::utl::UCBContentHelper::Kill( aOwnURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            throw io::IOException(); // TODO: error handling
    }

    //  XNameAccess

    uno::Any SAL_CALL FSStorage::getByName( const OUString& aName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( aName.isEmpty() )
            throw lang::IllegalArgumentException();

        uno::Any aResult;
        try
        {
            if ( !GetContent() )
                throw io::IOException(); // TODO: error handling

            INetURLObject aURL( m_pImpl->m_aURL );
            aURL.Append( aName );

            if ( ::utl::UCBContentHelper::IsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            {
                aResult <<= openStorageElement( aName, embed::ElementModes::READ );
            }
            else if ( ::utl::UCBContentHelper::IsDocument( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
            {
                aResult <<= openStreamElement( aName, embed::ElementModes::READ );
            }
            else
                throw container::NoSuchElementException(); // TODO:
        }
        catch (const container::NoSuchElementException&)
        {
            throw;
        }
        catch (const lang::WrappedTargetException&)
        {
            throw;
        }
        catch (const uno::RuntimeException&)
        {
            throw;
        }
        catch (const uno::Exception&)
        {
            uno::Any aCaught( ::cppu::getCaughtException() );
            throw lang::WrappedTargetException( "Can not open element!",
                                                static_cast< OWeakObject* >( this ),
                                                aCaught );
        }

        return aResult;
    }

    uno::Sequence< OUString > SAL_CALL FSStorage::getElementNames()
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        uno::Sequence< OUString > aResult;

        try
        {
            if ( !GetContent() )
                throw io::IOException(); // TODO: error handling

            uno::Sequence<OUString> aProps { "Title" };

            sal_Int32 nSize = 0;
            uno::Reference< sdbc::XResultSet > xResultSet = GetContent()->createCursor( aProps, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS );
            uno::Reference< sdbc::XRow > xRow( xResultSet, uno::UNO_QUERY );
            if ( xResultSet.is() )
            {
                // go through the list
                while ( xResultSet->next() )
                {
                    OUString aName( xRow->getString( 1 ) );
                    aResult.realloc( ++nSize );
                    aResult[nSize-1] = aName;
                }
            }
        }
        catch( const ucb::InteractiveIOException& r )
        {
            if ( r.Code == ucb::IOErrorCode_NOT_EXISTING )
                OSL_FAIL( "The folder does not exist!" );
            else
            {
                uno::Any aCaught( ::cppu::getCaughtException() );
                throw lang::WrappedTargetRuntimeException( "Can not open storage!",
                                                static_cast< OWeakObject* >( this ),
                                                aCaught );
            }
        }
        catch (const uno::RuntimeException&)
        {
            throw;
        }
        catch (const uno::Exception&)
        {
            uno::Any aCaught( ::cppu::getCaughtException() );
            throw lang::WrappedTargetRuntimeException( "Can not open storage!",
                                                static_cast< OWeakObject* >( this ),
                                                aCaught );
        }

        return aResult;
    }

    sal_Bool SAL_CALL FSStorage::hasByName( const OUString& aName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        try
        {
            if ( !GetContent() )
                throw io::IOException(); // TODO: error handling

            if ( aName.isEmpty() )
                throw lang::IllegalArgumentException();
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch ( uno::Exception& )
        {
            uno::Any aCaught( ::cppu::getCaughtException() );
            throw lang::WrappedTargetRuntimeException( "Can not open storage!",
                                                static_cast< OWeakObject* >( this ),
                                                aCaught );
        }

        INetURLObject aURL( m_pImpl->m_aURL );
        aURL.Append( aName );

        return ( ::utl::UCBContentHelper::IsFolder( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) )
          || ::utl::UCBContentHelper::IsDocument( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
    }

    uno::Type SAL_CALL FSStorage::getElementType()
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        // it is a multitype container
        return uno::Type();
    }

    sal_Bool SAL_CALL FSStorage::hasElements()
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        try
        {
            if ( !GetContent() )
                throw io::IOException(); // TODO: error handling

            uno::Sequence<OUString> aProps { "TargetURL" };

            uno::Reference< sdbc::XResultSet > xResultSet = GetContent()->createCursor( aProps, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS );
            return ( xResultSet.is() && xResultSet->next() );
        }
        catch (const uno::RuntimeException&)
        {
            throw;
        }
        catch (const uno::Exception& ex)
        {
            css::uno::Any anyEx = cppu::getCaughtException();
            throw lang::WrappedTargetRuntimeException( ex.Message,
                            nullptr, anyEx );
        }
    }

    //  XDisposable
    void SAL_CALL FSStorage::dispose()
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( m_pImpl->m_pListenersContainer )
        {
            lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
            m_pImpl->m_pListenersContainer->disposeAndClear( aSource );
        }

        m_pImpl.reset();
    }

    void SAL_CALL FSStorage::addEventListener(
                const uno::Reference< lang::XEventListener >& xListener )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !m_pImpl->m_pListenersContainer )
            m_pImpl->m_pListenersContainer.reset(new ::comphelper::OInterfaceContainerHelper2( m_aMutex ));

        m_pImpl->m_pListenersContainer->addInterface( xListener );
    }

    void SAL_CALL FSStorage::removeEventListener(
                const uno::Reference< lang::XEventListener >& xListener )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( m_pImpl->m_pListenersContainer )
            m_pImpl->m_pListenersContainer->removeInterface( xListener );
    }

    //  XPropertySet

    uno::Reference< beans::XPropertySetInfo > SAL_CALL FSStorage::getPropertySetInfo()
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        //TODO:
        return uno::Reference< beans::XPropertySetInfo >();
    }

    void SAL_CALL FSStorage::setPropertyValue( const OUString& aPropertyName, const uno::Any& )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( aPropertyName == "URL" || aPropertyName == "OpenMode" )
            throw beans::PropertyVetoException(); // TODO
        else
            throw beans::UnknownPropertyException(); // TODO
    }

    uno::Any SAL_CALL FSStorage::getPropertyValue( const OUString& aPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( aPropertyName == "URL" )
            return uno::makeAny( m_pImpl->m_aURL );
        else if ( aPropertyName == "OpenMode" )
            return uno::makeAny( m_pImpl->m_nMode );

        throw beans::UnknownPropertyException(); // TODO
    }

    void SAL_CALL FSStorage::addPropertyChangeListener(
                const OUString& /*aPropertyName*/,
                const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        //TODO:
    }

    void SAL_CALL FSStorage::removePropertyChangeListener(
                const OUString& /*aPropertyName*/,
                const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        //TODO:
    }

    void SAL_CALL FSStorage::addVetoableChangeListener(
                const OUString& /*PropertyName*/,
                const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        //TODO:
    }

    void SAL_CALL FSStorage::removeVetoableChangeListener(
                const OUString& /*PropertyName*/,
                const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        //TODO:
    }

    //  XHierarchicalStorageAccess
    uno::Reference< embed::XExtendedStorageStream > SAL_CALL FSStorage::openStreamElementByHierarchicalName( const OUString& sStreamPath, ::sal_Int32 nOpenMode )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( sStreamPath.toChar() == '/' )
            throw lang::IllegalArgumentException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        INetURLObject aBaseURL( m_pImpl->m_aURL );
        if ( !aBaseURL.setFinalSlash() )
            throw uno::RuntimeException();

        OUString aFileURL = INetURLObject::GetAbsURL(
                    aBaseURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                    sStreamPath );

        if ( ::utl::UCBContentHelper::IsFolder( aFileURL ) )
            throw io::IOException();

        if ( ( nOpenMode & embed::ElementModes::NOCREATE )
          && !::utl::UCBContentHelper::IsDocument( aFileURL ) )
            throw io::IOException(); // TODO:

        uno::Reference< ucb::XCommandEnvironment > xDummyEnv; // TODO: provide InteractionHandler if any
        uno::Reference< io::XStream > xResult;
        try
        {
            if ( nOpenMode & embed::ElementModes::WRITE )
            {
                if ( comphelper::isFileUrl( aFileURL ) )
                {
                    uno::Reference<ucb::XSimpleFileAccess3> xSimpleFileAccess(
                        ucb::SimpleFileAccess::create( m_pImpl->m_xContext ) );
                    uno::Reference< io::XStream > xStream =
                        xSimpleFileAccess->openFileReadWrite( aFileURL );

                    xResult = static_cast< io::XStream* >( new OFSStreamContainer( xStream ) );
                }
                else
                {
                    // TODO: test whether it really works for http and fwp
                    std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream( aFileURL,
                                                                              StreamMode::STD_WRITE );
                    if ( pStream )
                    {
                        if ( !pStream->GetError() )
                        {
                            uno::Reference< io::XStream > xStream =
                                uno::Reference < io::XStream >( new ::utl::OStreamWrapper( std::move(pStream) ) );
                            xResult = static_cast< io::XStream* >( new OFSStreamContainer( xStream ) );
                        }
                    }
                }

                if ( !xResult.is() )
                    throw io::IOException();

                if ( nOpenMode & embed::ElementModes::TRUNCATE )
                {
                    uno::Reference< io::XTruncate > xTrunc( xResult->getOutputStream(), uno::UNO_QUERY_THROW );
                    xTrunc->truncate();
                }
            }
            else
            {
                if ( ( nOpenMode & embed::ElementModes::TRUNCATE )
                  || !::utl::UCBContentHelper::IsDocument( aFileURL ) )
                    throw io::IOException(); // TODO: access denied

                ::ucbhelper::Content aResultContent( aFileURL, xDummyEnv, comphelper::getProcessComponentContext() );
                uno::Reference< io::XInputStream > xInStream = aResultContent.openStream();
                xResult = static_cast< io::XStream* >( new OFSInputStreamContainer( xInStream ) );
            }
        }
        catch( embed::InvalidStorageException& )
        {
            throw;
        }
        catch( lang::IllegalArgumentException& )
        {
            throw;
        }
        catch( packages::WrongPasswordException& )
        {
            throw;
        }
        catch( embed::StorageWrappedTargetException& )
        {
            throw;
        }
        catch( io::IOException& )
        {
            throw;
        }
        catch( uno::RuntimeException& )
        {
            throw;
        }
        catch( uno::Exception& )
        {
              uno::Any aCaught( ::cppu::getCaughtException() );
            throw embed::StorageWrappedTargetException("Can't copy raw stream",
                                                     uno::Reference< io::XInputStream >(),
                                                     aCaught );
        }

        return uno::Reference< embed::XExtendedStorageStream >( xResult, uno::UNO_QUERY_THROW );
    }

    uno::Reference< embed::XExtendedStorageStream > SAL_CALL FSStorage::openEncryptedStreamElementByHierarchicalName( const OUString& /*sStreamName*/, ::sal_Int32 /*nOpenMode*/, const OUString& /*sPassword*/ )
    {
        throw packages::NoEncryptionException();
    }

    void SAL_CALL FSStorage::removeStreamElementByHierarchicalName( const OUString& sStreamPath )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !m_pImpl )
            throw lang::DisposedException();

        if ( !GetContent() )
            throw io::IOException(); // TODO: error handling

        // TODO/LATER: may need possibility to create folder if it was removed, since the folder can not be locked
        INetURLObject aBaseURL( m_pImpl->m_aURL );
        if ( !aBaseURL.setFinalSlash() )
            throw uno::RuntimeException();

        OUString aFileURL = INetURLObject::GetAbsURL(
                    aBaseURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
                    sStreamPath );

        if ( !::utl::UCBContentHelper::IsDocument( aFileURL ) )
        {
            if ( ::utl::UCBContentHelper::IsFolder( aFileURL ) )
                throw lang::IllegalArgumentException();
            else
                throw container::NoSuchElementException(); // TODO:
        }

        if ( !::utl::UCBContentHelper::Kill( aFileURL ) )
            throw io::IOException(); // TODO: error handling
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V524 It is odd that the body of 'removeVetoableChangeListener' function is fully equivalent to the body of 'addVetoableChangeListener' function.

↑ V524 It is odd that the body of 'removePropertyChangeListener' function is fully equivalent to the body of 'addPropertyChangeListener' function.

↑ V547 Expression '!GetContent()' is always false.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.

↑ V547 Expression '!GetContent()' is always false.