    /* -*- 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
     **************************************************************************

     - optimize transfer command. "Move" should be implementable much more
       efficient!

     **************************************************************************

     - Root Folder vs. 'normal' Folder
        - root doesn't support command 'delete'
        - root doesn't support command 'insert'
        - root needs not created via XContentCreator - queryContent with root
          folder id ( HIERARCHY_ROOT_FOLDER_URL ) always returns a value != 0
        - root has no parent.

     *************************************************************************/
    #include <osl/diagnose.h>

    #include <rtl/ustring.h>
    #include <rtl/ustring.hxx>
    #include <com/sun/star/beans/IllegalTypeException.hpp>
    #include <com/sun/star/beans/PropertyAttribute.hpp>
    #include <com/sun/star/beans/PropertyExistException.hpp>
    #include <com/sun/star/beans/PropertyState.hpp>
    #include <com/sun/star/beans/PropertyValue.hpp>
    #include <com/sun/star/beans/XPropertyAccess.hpp>
    #include <com/sun/star/lang/IllegalAccessException.hpp>
    #include <com/sun/star/sdbc/XRow.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/MissingPropertiesException.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/TransferInfo.hpp>
    #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
    #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
    #include <com/sun/star/ucb/XCommandInfo.hpp>
    #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
    #include <com/sun/star/uno/Any.hxx>
    #include <com/sun/star/uno/Sequence.hxx>
    #include <comphelper/propertysequence.hxx>
    #include <cppuhelper/queryinterface.hxx>
    #include <ucbhelper/contentidentifier.hxx>
    #include <ucbhelper/propertyvalueset.hxx>
    #include <ucbhelper/cancelcommandexecution.hxx>
    #include <ucbhelper/macros.hxx>
    #include "hierarchycontent.hxx"
    #include "hierarchyprovider.hxx"
    #include "dynamicresultset.hxx"
    #include "hierarchyuri.hxx"

    #include "../inc/urihelper.hxx"

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

    // HierarchyContent Implementation.

    // static ( "virtual" ctor )
    HierarchyContent* HierarchyContent::create(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier )
    {
        // Fail, if content does not exist.
        HierarchyContentProperties aProps;
        if ( !loadData( rxContext, pProvider, Identifier, aProps ) )
            return nullptr;

        return new HierarchyContent( rxContext, pProvider, Identifier, aProps );
    }

    // static ( "virtual" ctor )
    HierarchyContent* HierarchyContent::create(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier,
                const ucb::ContentInfo& Info )
    {
        if ( Info.Type.isEmpty() )
            return nullptr;

        if ( Info.Type != HIERARCHY_FOLDER_CONTENT_TYPE && Info.Type != HIERARCHY_LINK_CONTENT_TYPE )
            return nullptr;

        return new HierarchyContent( rxContext, pProvider, Identifier, Info );
    }

    HierarchyContent::HierarchyContent(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier,
                const HierarchyContentProperties& rProps )
    : ContentImplHelper( rxContext, pProvider, Identifier ),
      m_aProps( rProps ),
      m_eState( PERSISTENT ),
      m_pProvider( pProvider ),
      m_bCheckedReadOnly( false ),
      m_bIsReadOnly( true )
    {
        setKind( Identifier );
    }

    HierarchyContent::HierarchyContent(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier,
                const ucb::ContentInfo& Info )
      : ContentImplHelper( rxContext, pProvider, Identifier ),
      m_aProps( Info.Type == HIERARCHY_FOLDER_CONTENT_TYPE ? HierarchyEntryData::FOLDER : HierarchyEntryData::LINK ),
      m_eState( TRANSIENT ),
      m_pProvider( pProvider ),
      m_bCheckedReadOnly( false ),
      m_bIsReadOnly( true )
    {
        setKind( Identifier );
    }

    // virtual
    HierarchyContent::~HierarchyContent()
    {
    }

    // XInterface methods.

    // virtual
    void SAL_CALL HierarchyContent::acquire()
        throw( )
    {
        ContentImplHelper::acquire();
    }

    // virtual
    void SAL_CALL HierarchyContent::release()
        throw( )
    {
        ContentImplHelper::release();
    }

    // virtual
    uno::Any SAL_CALL HierarchyContent::queryInterface( const uno::Type & rType )
    {
        uno::Any aRet = ContentImplHelper::queryInterface( rType );

        if ( !aRet.hasValue() )
        {
            // Note: isReadOnly may be relative expensive. So avoid calling it
            //       unless it is really necessary.
            aRet = cppu::queryInterface(
                    rType, static_cast< ucb::XContentCreator * >( this ) );
            if ( aRet.hasValue() )
            {
                if ( !isFolder() || isReadOnly() )
                    return uno::Any();
            }
        }

        return aRet;
    }

    // XTypeProvider methods.

    XTYPEPROVIDER_COMMON_IMPL( HierarchyContent );

    // virtual
    uno::Sequence< uno::Type > SAL_CALL HierarchyContent::getTypes()
    {
        if ( isFolder() && !isReadOnly() )
        {
            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 HierarchyContent::getImplementationName()
    {
        return OUString( "com.sun.star.comp.ucb.HierarchyContent" );
    }

    // virtual
    uno::Sequence< OUString > SAL_CALL
    HierarchyContent::getSupportedServiceNames()
    {
        uno::Sequence< OUString > aSNS( 1 );

        if ( m_eKind == LINK )
            aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyLinkContent";
        else if ( m_eKind == FOLDER )
            aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyFolderContent";
        else
            aSNS.getArray()[ 0 ] = "com.sun.star.ucb.HierarchyRootFolderContent";

        return aSNS;
    }

    // XContent methods.

    // virtual
    OUString SAL_CALL HierarchyContent::getContentType()
    {
        return m_aProps.getContentType();
    }

    // virtual
    uno::Reference< ucb::XContentIdentifier > SAL_CALL
    HierarchyContent::getIdentifier()
    {
        // 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 HierarchyContent::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::makeAny( 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::makeAny( lang::IllegalArgumentException(
                                        "Wrong argument type!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 ) ),
                    Environment );
                // Unreachable
            }

            if ( !aProperties.getLength() )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( 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" && isFolder() )
        {

            // open command for a folder content

            ucb::OpenCommandArgument2 aOpenCommand;
            if ( !( aCommand.Argument >>= aOpenCommand ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( lang::IllegalArgumentException(
                                        "Wrong argument type!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 ) ),
                    Environment );
                // Unreachable
            }

            uno::Reference< ucb::XDynamicResultSet > xSet
                    = new DynamicResultSet( m_xContext, this, aOpenCommand );
            aRet <<= xSet;
        }
        else if ( aCommand.Name == "insert" && ( m_eKind != ROOT ) && !isReadOnly() )
        {

            // insert
            //  ( Not available at root folder )

            ucb::InsertCommandArgument aArg;
            if ( !( aCommand.Argument >>= aArg ) )
            {
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( 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( nNameClash, Environment );
        }
        else if ( aCommand.Name == "delete" && ( m_eKind != ROOT ) && !isReadOnly() )
        {

            // delete
            //  ( Not available at root folder )

            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" && isFolder() && !isReadOnly() )
        {

            // transfer
            //      ( Not available at link objects )

            ucb::TransferInfo aInfo;
            if ( !( aCommand.Argument >>= aInfo ) )
            {
                OSL_FAIL( "Wrong argument type!" );
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( lang::IllegalArgumentException(
                                        "Wrong argument type!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 ) ),
                    Environment );
                // Unreachable
            }

            transfer( aInfo, Environment );
        }
        else if ( aCommand.Name == "createNewContent" && isFolder() && !isReadOnly() )
        {

            // createNewContent
            //      ( Not available at link objects )

            ucb::ContentInfo aInfo;
            if ( !( aCommand.Argument >>= aInfo ) )
            {
                OSL_FAIL( "Wrong argument type!" );
                ucbhelper::cancelCommandExecution(
                    uno::makeAny( lang::IllegalArgumentException(
                                        "Wrong argument type!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 ) ),
                    Environment );
                // Unreachable
            }

            aRet <<= createNewContent( aInfo );
        }
        else
        {

            // Unsupported command

            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedCommandException(
                                    OUString(),
                                    static_cast< cppu::OWeakObject * >( this ) ) ),
                Environment );
            // Unreachable
        }

        return aRet;
    }

    // virtual
    void SAL_CALL HierarchyContent::abort( sal_Int32 /*CommandId*/ )
    {
        // @@@ Generally, no action takes much time...
    }

    // XContentCreator methods.

    // virtual
    uno::Sequence< ucb::ContentInfo > SAL_CALL
    HierarchyContent::queryCreatableContentsInfo()
    {
        return m_aProps.getCreatableContentsInfo();
    }

    // virtual
    uno::Reference< ucb::XContent > SAL_CALL
    HierarchyContent::createNewContent( const ucb::ContentInfo& Info )
    {
        if ( isFolder() )
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );

            if ( Info.Type.isEmpty() )
                return uno::Reference< ucb::XContent >();

            bool bCreateFolder = Info.Type == HIERARCHY_FOLDER_CONTENT_TYPE;

            if ( !bCreateFolder && Info.Type != HIERARCHY_LINK_CONTENT_TYPE )
                return uno::Reference< ucb::XContent >();

            OUString aURL = m_xIdentifier->getContentIdentifier();

            OSL_ENSURE( !aURL.isEmpty(),
                        "HierarchyContent::createNewContent - empty identifier!" );

            if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
                aURL += "/";

            if ( bCreateFolder )
                aURL += "New_Folder";
            else
                aURL += "New_Link";

            uno::Reference< ucb::XContentIdentifier > xId
                = new ::ucbhelper::ContentIdentifier( aURL );

            return create( m_xContext, m_pProvider, xId, Info );
        }
        else
        {
            OSL_FAIL( "createNewContent called on non-folder object!" );
            return uno::Reference< ucb::XContent >();
        }
    }

    // virtual
    OUString HierarchyContent::getParentURL()
    {
        HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
        return aUri.getParentUri();
    }

    //static
    bool HierarchyContent::hasData(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier )
    {
        OUString aURL = Identifier->getContentIdentifier();

        // Am I a root folder?
        HierarchyUri aUri( aURL );
        if ( aUri.isRootFolder() )
        {
            // hasData must always return 'true' for root folder
            // even if no persistent data exist!!!
            return true;
        }

        return HierarchyEntry( rxContext, pProvider, aURL ).hasData();
    }

    //static
    bool HierarchyContent::loadData(
                const uno::Reference< uno::XComponentContext >& rxContext,
                HierarchyContentProvider* pProvider,
                const uno::Reference< ucb::XContentIdentifier >& Identifier,
                HierarchyContentProperties& rProps )
    {
        OUString aURL = Identifier->getContentIdentifier();

        // Am I a root folder?
        HierarchyUri aUri( aURL );
        if ( aUri.isRootFolder() )
        {
            rProps = HierarchyContentProperties( HierarchyEntryData::FOLDER );
        }
        else
        {
            HierarchyEntry aEntry( rxContext, pProvider, aURL );
            HierarchyEntryData aData;
            if ( !aEntry.getData( aData ) )
                return false;

            rProps = HierarchyContentProperties( aData );
        }
        return true;
    }

    bool HierarchyContent::storeData()
    {
        HierarchyEntry aEntry(
                m_xContext, m_pProvider, m_xIdentifier->getContentIdentifier() );
        return aEntry.setData( m_aProps.getHierarchyEntryData() );
    }

    void HierarchyContent::renameData(
                const uno::Reference< ucb::XContentIdentifier >& xOldId,
                const uno::Reference< ucb::XContentIdentifier >& xNewId )
    {
        HierarchyEntry aEntry(
                m_xContext, m_pProvider, xOldId->getContentIdentifier() );
        aEntry.move( xNewId->getContentIdentifier(),
                            m_aProps.getHierarchyEntryData() );
    }

    bool HierarchyContent::removeData()
    {
        HierarchyEntry aEntry(
            m_xContext, m_pProvider, m_xIdentifier->getContentIdentifier() );
        return aEntry.remove();
    }

    void HierarchyContent::setKind(
                const uno::Reference< ucb::XContentIdentifier >& Identifier )
    {
        if ( m_aProps.getIsFolder() )
        {
            // Am I a root folder?
            HierarchyUri aUri( Identifier->getContentIdentifier() );
            if ( aUri.isRootFolder() )
                m_eKind = ROOT;
            else
                m_eKind = FOLDER;
        }
        else
            m_eKind = LINK;
    }

    bool HierarchyContent::isReadOnly()
    {
        if ( !m_bCheckedReadOnly )
        {
            osl::Guard< osl::Mutex > aGuard( m_aMutex );
            if ( !m_bCheckedReadOnly )
            {
                m_bCheckedReadOnly = true;
                m_bIsReadOnly      = true;

                HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
                uno::Reference< lang::XMultiServiceFactory > xConfigProv
                    = m_pProvider->getConfigProvider( aUri.getService() );
                if ( xConfigProv.is() )
                {
                    uno::Sequence< OUString > aNames
                        = xConfigProv->getAvailableServiceNames();
                    sal_Int32 nCount = aNames.getLength();
                    for ( sal_Int32 n = 0; n < nCount; ++n )
                    {
                        if ( aNames[ n ] == "com.sun.star.ucb.HierarchyDataReadWriteAccess" )
                        {
                            m_bIsReadOnly = false;
                            break;
                        }
                    }
                }
            }
        }

        return m_bIsReadOnly;
    }

    uno::Reference< ucb::XContentIdentifier >
    HierarchyContent::makeNewIdentifier( const OUString& rTitle )
    {
        osl::Guard< osl::Mutex > aGuard( m_aMutex );

        // Assemble new content identifier...
        HierarchyUri aUri( m_xIdentifier->getContentIdentifier() );
        OUString aNewURL = aUri.getParentUri();
        aNewURL += "/";
        aNewURL += ::ucb_impl::urihelper::encodeSegment( rTitle );

        return uno::Reference< ucb::XContentIdentifier >(
            new ::ucbhelper::ContentIdentifier( aNewURL ) );
    }

    void HierarchyContent::queryChildren( HierarchyContentRefVector& rChildren )
    {
        if ( ( m_eKind != FOLDER ) && ( m_eKind != ROOT ) )
            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();

        ::ucbhelper::ContentRefList::const_iterator it  = aAllContents.begin();
        ::ucbhelper::ContentRefList::const_iterator end = aAllContents.end();

        while ( it != end )
        {
            ::ucbhelper::ContentImplHelperRef xChild = (*it);
            OUString aChildURL
                = xChild->getIdentifier()->getContentIdentifier();

            // Is aURL a prefix of aChildURL?
            if ( ( aChildURL.getLength() > nLen ) &&
                 ( aChildURL.startsWith( aURL ) ) )
            {
                sal_Int32 nPos = nLen;
                nPos = aChildURL.indexOf( '/', nPos );

                if ( ( nPos == -1 ) ||
                     ( nPos == ( aChildURL.getLength() - 1 ) ) )
                {
                    // No further slashes/ only a final slash. It's a child!
                    rChildren.emplace_back(
                            static_cast< HierarchyContent * >( xChild.get() ) );
                }
            }
            ++it;
        }
    }

    bool HierarchyContent::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( "HierarchyContent::exchangeIdentity - Not persistent!" );
            return false;
        }

        // Am I the root folder?
        if ( m_eKind == ROOT )
        {
            OSL_FAIL( "HierarchyContent::exchangeIdentity - "
                                   "Not supported by root folder!" );
            return false;
        }

        // Exchange own identitity.

        // Fail, if a content with given id already exists.
        if ( !hasData( xNewId ) )
        {
            OUString aOldURL = m_xIdentifier->getContentIdentifier();

            aGuard.clear();
            if ( exchange( xNewId ) )
            {
                if ( m_eKind == FOLDER )
                {
                    // Process instantiated children...

                    HierarchyContentRefVector aChildren;
                    queryChildren( aChildren );

                    HierarchyContentRefVector::const_iterator it  = aChildren.begin();

                    while ( it != aChildren.end() )
                    {
                        HierarchyContentRef xChild = (*it);

                        // 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;

                        ++it;
                    }
                }
                return true;
            }
        }

        OSL_FAIL( "HierarchyContent::exchangeIdentity - "
                    "Panic! Cannot exchange identity!" );
        return false;
    }

    // static
    uno::Reference< sdbc::XRow > HierarchyContent::getPropertyValues(
                    const uno::Reference< uno::XComponentContext >& rxContext,
                    const uno::Sequence< beans::Property >& rProperties,
                    const HierarchyContentProperties& rData,
                    HierarchyContentProvider* pProvider,
                    const OUString& rContentId )
    {
        // Note: Empty sequence means "get values of all supported properties".

        rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
            = new ::ucbhelper::PropertyValueSet( rxContext );

        sal_Int32 nCount = rProperties.getLength();
        if ( nCount )
        {
            uno::Reference< beans::XPropertySet > xAdditionalPropSet;
            bool bTriedToGetAdditionalPropSet = false;

            const beans::Property* pProps = rProperties.getConstArray();
            for ( sal_Int32 n = 0; n < nCount; ++n )
            {
                const beans::Property& rProp = pProps[ n ];

                // 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::makeAny( rData.getCreatableContentsInfo() ) );
                }
                else if ( rProp.Name == "TargetURL" )
                {
                    // TargetURL is only supported by links.

                    if ( rData.getIsDocument() )
                        xRow->appendString( rProp, rData.getTargetURL() );
                    else
                        xRow->appendVoid( rProp );
                }
                else
                {
                    // Not a Core Property! Maybe it's an Additional Core Property?!

                    if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
                    {
                        xAdditionalPropSet.set(
                                pProvider->getAdditionalPropertySet( rContentId,
                                                                     false ),
                                uno::UNO_QUERY );
                        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() );
            xRow->appendString (
                beans::Property( "Title",
                          -1,
                          cppu::UnoType<OUString>::get(),
                              // @@@ Might actually be read-only!
                          beans::PropertyAttribute::BOUND ),
                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() );

            if ( rData.getIsDocument() )
                xRow->appendString(
                    beans::Property( "TargetURL",
                              -1,
                              cppu::UnoType<OUString>::get(),
                              // @@@ Might actually be read-only!
                              beans::PropertyAttribute::BOUND ),
                    rData.getTargetURL() );
            xRow->appendObject(
                beans::Property(
                    "CreatableContentsInfo",
                    -1,
                    cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
                    beans::PropertyAttribute::BOUND
                    | beans::PropertyAttribute::READONLY ),
                uno::makeAny( rData.getCreatableContentsInfo() ) );

            // Append all Additional Core Properties.

            uno::Reference< beans::XPropertySet > xSet(
                pProvider->getAdditionalPropertySet( rContentId, false ),
                uno::UNO_QUERY );
            xRow->appendPropertySet( xSet );
        }

        return uno::Reference< sdbc::XRow >( xRow.get() );
    }

    uno::Reference< sdbc::XRow > HierarchyContent::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 > HierarchyContent::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() );
        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;
        OUString aOldName;
        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!
                aRet[ n ] <<= lang::IllegalAccessException(
                                "Property is read-only!",
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else if ( rValue.Name == "IsDocument" )
            {
                // Read-only property!
                aRet[ n ] <<= lang::IllegalAccessException(
                                "Property is read-only!",
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else if ( rValue.Name == "IsFolder" )
            {
                // Read-only property!
                aRet[ n ] <<= lang::IllegalAccessException(
                                "Property is read-only!",
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else if ( rValue.Name == "CreatableContentsInfo" )
            {
                // Read-only property!
                aRet[ n ] <<= lang::IllegalAccessException(
                                "Property is read-only!",
                                static_cast< cppu::OWeakObject * >( this ) );
            }
            else if ( rValue.Name == "Title" )
            {
                if ( isReadOnly() )
                {
                    aRet[ 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();
                                aOldName  = m_aProps.getName();

                                m_aProps.setTitle( aNewValue );
                                m_aProps.setName(
                                    ::ucb_impl::urihelper::encodeSegment(
                                        aNewValue ) );

                                // property change event will be set later...

                                // remember position within sequence of values
                                // (for error handling).
                                nTitlePos = n;
                            }
                        }
                        else
                        {
                            aRet[ n ] <<= lang::IllegalArgumentException(
                                        "Empty title not allowed!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 );
                        }
                    }
                    else
                    {
                        aRet[ n ] <<= beans::IllegalTypeException(
                                    "Property value has wrong type!",
                                    static_cast< cppu::OWeakObject * >( this ) );
                    }
                }
            }
            else if ( rValue.Name == "TargetURL" )
            {
                if ( isReadOnly() )
                {
                    aRet[ n ] <<= lang::IllegalAccessException(
                                    "Property is read-only!",
                                    static_cast< cppu::OWeakObject * >( this ) );
                }
                else
                {
                    // TargetURL is only supported by links.

                    if ( m_eKind == LINK )
                    {
                        OUString aNewValue;
                        if ( rValue.Value >>= aNewValue )
                        {
                            // No empty target URL's!
                            if ( !aNewValue.isEmpty() )
                            {
                                if ( aNewValue != m_aProps.getTargetURL() )
                                {
                                    aEvent.PropertyName = rValue.Name;
                                    aEvent.OldValue <<= m_aProps.getTargetURL();
                                    aEvent.NewValue <<= aNewValue;

                                    aChanges.getArray()[ nChanged ] = aEvent;

                                    m_aProps.setTargetURL( aNewValue );
                                    nChanged++;
                                }
                            }
                            else
                            {
                                aRet[ n ] <<= lang::IllegalArgumentException(
                                        "Empty target URL not allowed!",
                                        static_cast< cppu::OWeakObject * >( this ),
                                        -1 );
                            }
                        }
                        else
                        {
                            aRet[ n ] <<= beans::IllegalTypeException(
                                    "Property value has wrong type!",
                                    static_cast< cppu::OWeakObject * >( this ) );
                        }
                    }
                    else
                    {
                        aRet[ n ] <<= beans::UnknownPropertyException(
                                    "TargetURL only supported by links!",
                                    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 )
                    {
                        aRet[ n ] <<= e;
                    }
                    catch ( lang::WrappedTargetException const & e )
                    {
                        aRet[ n ] <<= e;
                    }
                    catch ( beans::PropertyVetoException const & e )
                    {
                        aRet[ n ] <<= e;
                    }
                    catch ( lang::IllegalArgumentException const & e )
                    {
                        aRet[ n ] <<= e;
                    }
                }
                else
                {
                    aRet[ 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 );
                m_aProps.setName ( aOldName );

                aOldTitle.clear();
                aOldName.clear();

                // Set error .
                aRet[ 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::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;
    }

    void HierarchyContent::insert( sal_Int32 nNameClashResolve,
                                   const uno::Reference<
                                        ucb::XCommandEnvironment > & xEnv )
    {
        osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

        // Am I the root folder?
        if ( m_eKind == ROOT )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedCommandException(
                                    "Not supported by root folder!",
                                    static_cast< cppu::OWeakObject * >( this ) ) ),
                xEnv );
            // Unreachable
        }

        // Check, if all required properties were set.
        if ( m_aProps.getTitle().isEmpty() )
        {
            uno::Sequence<OUString> aProps { "Title" };
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::MissingPropertiesException(
                                    OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    aProps ) ),
                xEnv );
            // Unreachable
        }

        // Assemble new content identifier...

        uno::Reference< ucb::XContentIdentifier > xId
            = makeNewIdentifier( m_aProps.getTitle() );

        // Handle possible name clash...

        switch ( nNameClashResolve )
        {
            // fail.
            case ucb::NameClash::ERROR:
                if ( hasData( xId ) )
                {
                    ucbhelper::cancelCommandExecution(
                        uno::makeAny(
                            ucb::NameClashException(
                                OUString(),
                                static_cast< cppu::OWeakObject * >( this ),
                                task::InteractionClassification_ERROR,
                                m_aProps.getTitle() ) ),
                        xEnv );
                    // Unreachable
                }
                break;

            // replace existing object.
            case ucb::NameClash::OVERWRITE:
                break;

            // "invent" a new valid title.
            case ucb::NameClash::RENAME:
                if ( hasData( xId ) )
                {
                    sal_Int32 nTry = 0;

                    do
                    {
                        OUString aNewId = xId->getContentIdentifier();
                        aNewId += "_";
                        aNewId += OUString::number( ++nTry );
                        xId = new ::ucbhelper::ContentIdentifier( aNewId );
                    }
                    while ( hasData( xId ) && ( nTry < 1000 ) );

                    if ( nTry == 1000 )
                    {
                        ucbhelper::cancelCommandExecution(
                            uno::makeAny(
                                ucb::UnsupportedNameClashException(
                                    "Unable to resolve name clash!",
                                    static_cast< cppu::OWeakObject * >( this ),
                                    nNameClashResolve ) ),
                        xEnv );
                    // Unreachable
                    }
                    else
                    {
                        OUString aNewTitle( m_aProps.getTitle() );
                        aNewTitle += "_";
                        aNewTitle += OUString::number( nTry );
                        m_aProps.setTitle( aNewTitle );
                    }
                }
                break;

            case ucb::NameClash::KEEP: // deprecated
            case ucb::NameClash::ASK:
            default:
                if ( hasData( xId ) )
                {
                    ucbhelper::cancelCommandExecution(
                        uno::makeAny(
                            ucb::UnsupportedNameClashException(
                                    OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    nNameClashResolve ) ),
                        xEnv );
                    // Unreachable
                }
                break;
        }

        // Identifier changed?
        bool bNewId = ( xId->getContentIdentifier()
                                != m_xIdentifier->getContentIdentifier() );
        m_xIdentifier = xId;

        if ( !storeData() )
        {
            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 )
        {
            aGuard.clear();
            inserted();
        }
    }

    void HierarchyContent::destroy( bool bDeletePhysical,
                                    const uno::Reference<
                                        ucb::XCommandEnvironment > & xEnv )
    {
        // @@@ take care about bDeletePhysical -> trashcan support

        osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

        uno::Reference< ucb::XContent > xThis = this;

        // Persistent?
        if ( m_eState != PERSISTENT )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedCommandException(
                                    "Not persistent!",
                                    static_cast< cppu::OWeakObject * >( this ) ) ),
                xEnv );
            // Unreachable
        }

        // Am I the root folder?
        if ( m_eKind == ROOT )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::UnsupportedCommandException(
                                    "Not supported by root folder!",
                                    static_cast< cppu::OWeakObject * >( this ) ) ),
                xEnv );
            // Unreachable
        }

        m_eState = DEAD;

        aGuard.clear();
        deleted();

        if ( m_eKind == FOLDER )
        {
            // Process instantiated children...

            HierarchyContentRefVector aChildren;
            queryChildren( aChildren );

            for ( auto & child : aChildren)
            {
                child->destroy( bDeletePhysical, xEnv );
            }
        }
    }

    void HierarchyContent::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::makeAny( ucb::UnsupportedCommandException(
                                    "Not persistent!",
                                    static_cast< cppu::OWeakObject * >( this ) ) ),
                xEnv );
            // Unreachable
        }

        // Is source a hierarchy content?
        if ( !rInfo.SourceURL.startsWith( HIERARCHY_URL_SCHEME ":/" ) )
        {
            ucbhelper::cancelCommandExecution(
                uno::makeAny( ucb::InteractiveBadTransferURLException(
                                OUString(),
                                static_cast< cppu::OWeakObject * >( this ) ) ),
                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
            }
        }

        // 0) Obtain content object for source.

        uno::Reference< ucb::XContentIdentifier > xId
            = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );

        // Note: The static cast is okay here, because its sure that
        //       m_xProvider is always the HierarchyContentProvider.
        rtl::Reference< HierarchyContent > xSource;

        try
        {
            xSource = static_cast< HierarchyContent * >(
                            m_xProvider->queryContent( xId ).get() );
        }
        catch ( ucb::IllegalIdentifierException const & )
        {
            // queryContent
        }

        if ( !xSource.is() )
        {
            uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
            {
                {"Uri", uno::Any(xId->getContentIdentifier())}
            }));
            ucbhelper::cancelCommandExecution(
                ucb::IOErrorCode_CANT_READ,
                aArgs,
                xEnv,
                "Cannot instantiate source object!",
                this );
            // Unreachable
        }

        // 1) Create new child content.

        OUString aType = xSource->isFolder()
            ? OUString( HIERARCHY_FOLDER_CONTENT_TYPE )
            : OUString( HIERARCHY_LINK_CONTENT_TYPE );
        ucb::ContentInfo aContentInfo;
        aContentInfo.Type = aType;
        aContentInfo.Attributes = 0;

        // Note: The static cast is okay here, because its sure that
        //       createNewContent always creates a HierarchyContent.
        rtl::Reference< HierarchyContent > xTarget
            = static_cast< HierarchyContent * >(
                createNewContent( aContentInfo ).get() );
        if ( !xTarget.is() )
        {
            uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
            {
                {"Folder", uno::Any(aId)}
            }));
            ucbhelper::cancelCommandExecution(
                ucb::IOErrorCode_CANT_CREATE,
                aArgs,
                xEnv,
                "XContentCreator::createNewContent failed!",
                this );
            // Unreachable
        }

        // 2) Copy data from source content to child content.

        uno::Sequence< beans::Property > aSourceProps
                        = xSource->getPropertySetInfo( xEnv )->getProperties();
        sal_Int32 nCount = aSourceProps.getLength();

        if ( nCount )
        {
            bool bHadTitle = rInfo.NewTitle.isEmpty();

            // Get all source values.
            uno::Reference< sdbc::XRow > xRow
                = xSource->getPropertyValues( aSourceProps );

            uno::Sequence< beans::PropertyValue > aValues( nCount );
            beans::PropertyValue* pValues = aValues.getArray();

            const beans::Property* pProps = aSourceProps.getConstArray();
            for ( sal_Int32 n = 0; n < nCount; ++n )
            {
                const beans::Property& rProp  = pProps[ n ];
                beans::PropertyValue&  rValue = pValues[ n ];

                rValue.Name   = rProp.Name;
                rValue.Handle = rProp.Handle;

                if ( !bHadTitle && rProp.Name == "Title" )
                {
                    // Set new title instead of original.
                    bHadTitle = true;
                    rValue.Value <<= rInfo.NewTitle;
                }
                else
                    rValue.Value = xRow->getObject(
                                    n + 1,
                                    uno::Reference< container::XNameAccess >() );

                rValue.State = beans::PropertyState_DIRECT_VALUE;

                if ( rProp.Attributes & beans::PropertyAttribute::REMOVABLE )
                {
                    // Add Additional Core Property.
                    try
                    {
                        xTarget->addProperty( rProp.Name,
                                              rProp.Attributes,
                                              rValue.Value );
                    }
                    catch ( beans::PropertyExistException const & )
                    {
                    }
                    catch ( beans::IllegalTypeException const & )
                    {
                    }
                    catch ( lang::IllegalArgumentException const & )
                    {
                    }
                }
            }

            // Set target values.
            xTarget->setPropertyValues( aValues, xEnv );
        }

        // 3) Commit (insert) child.

        xTarget->insert( rInfo.NameClash, xEnv );

        // 4) Transfer (copy) children of source.

        if ( xSource->isFolder() )
        {
            HierarchyEntry aFolder(
                m_xContext, m_pProvider, xId->getContentIdentifier() );
            HierarchyEntry::iterator it;

            while ( aFolder.next( it ) )
            {
                const HierarchyEntryData& rResult = *it;

                OUString aChildId = xId->getContentIdentifier();
                if ( ( aChildId.lastIndexOf( '/' ) + 1 ) != aChildId.getLength() )
                    aChildId += "/";

                aChildId += rResult.getName();

                ucb::TransferInfo aInfo;
                aInfo.MoveData  = false;
                aInfo.NewTitle.clear();
                aInfo.SourceURL = aChildId;
                aInfo.NameClash = rInfo.NameClash;

                // Transfer child to target.
                xTarget->transfer( aInfo, xEnv );
            }
        }

        // 5) Destroy source ( when moving only ) .

        if ( rInfo.MoveData )
        {
            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(xSource->m_xIdentifier->getContentIdentifier())}
                }));
                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.
            xSource->removeAdditionalPropertySet();
        }
    }

    // HierarchyContentProperties Implementation.

    uno::Sequence< ucb::ContentInfo >
    HierarchyContentProperties::getCreatableContentsInfo() const
    {
        if ( getIsFolder() )
        {
            uno::Sequence< ucb::ContentInfo > aSeq( 2 );

            // Folder.
            aSeq.getArray()[ 0 ].Type = HIERARCHY_FOLDER_CONTENT_TYPE;
            aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;

            uno::Sequence< beans::Property > aFolderProps( 1 );
            aFolderProps.getArray()[ 0 ] = beans::Property(
                        "Title",
                        -1,
                        cppu::UnoType<OUString>::get(),
                        beans::PropertyAttribute::BOUND );
            aSeq.getArray()[ 0 ].Properties = aFolderProps;

            // Link.
            aSeq.getArray()[ 1 ].Type = HIERARCHY_LINK_CONTENT_TYPE;
            aSeq.getArray()[ 1 ].Attributes = ucb::ContentInfoAttribute::KIND_LINK;

            uno::Sequence< beans::Property > aLinkProps( 2 );
            aLinkProps.getArray()[ 0 ] = beans::Property(
                        "Title",
                        -1,
                        cppu::UnoType<OUString>::get(),
                        beans::PropertyAttribute::BOUND );
            aLinkProps.getArray()[ 1 ] = beans::Property(
                        "TargetURL",
                        -1,
                        cppu::UnoType<OUString>::get(),
                        beans::PropertyAttribute::BOUND );
            aSeq.getArray()[ 1 ].Properties = aLinkProps;

            return aSeq;
        }
        else
        {
            return uno::Sequence< ucb::ContentInfo >( 0 );
        }
    }

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

↑ V547 Expression '!m_bCheckedReadOnly' is always true.